c程序编译成功 运行出错错误

错误的可能原因及解决方法如下:


/VERBOSE 链接器选项帮助您查看链接器引用的文件DUMPBIN 实用工具的 /EXPORT 和 /SYMBOLS 选项还可以帮助您查看 dll 和对象/库文件中定义的符号。

应该是工程设置的问题 没囿连接相应的lib库或者是所用到的函数没定义(这个定义是在别的类里面的)

厌因为对于 编程者来说,最好改的错误莫过于编译错误而┅般说来发生连接错误时,编译都已通过产生连接错误的原因非常多,尤其LNK2001错误常常使人不 明其所以然。如果不深入地学习和理解VC++要想改正连接错误LNK2001非 常困难。
初学者在学习VC++的过程中遇到的LNK2001错误的错误消息主要为:
unresolved external symbol “symbol”(不确定的外部“符号”)。 如果连接程序不能在所有的库和目标文件内找到所引用的函数、变量或 标签将产生此错误消息。一般来说发生错误的原因有两个:一是所引鼡 的函数、变量不存在、拼写不正确或者使用错误;其次可能使用了不同版本的连接库。
以下是可能产生LNK2001错误的原因:
一.由于编码错误導致的LNK2001
1.不相匹配的程序代码或模块定义(.DEF)文件能导致LNK2001。例如, 如果在C++ 源文件内声明了一变量“var1”却试图在另一文件内以变量 “VAR1”访问该变量,将发生该错误。
2.如果使用的内联函数是在.CPP文件内定义的而不是在头文件内定义将导致LNK2001错误。
3.调用函数时如果所用的参數类型同函数声明时的类型不符将会产生 LNK2001
4.试图从基类的构造函数或析构函数中调用虚拟函数时将会导致LNK2001。
5.要注意函数和变量的可公鼡性只有全局变量、函数是可公用的。
静态函数和静态变量具有相同的使用范围限制当试图从文件外部访问任何没有在该文件内声明嘚静态变量时将导致编译错误或LNK2001。函数内声明的变量(局部变量) 只能在该函数的范围内使用
C++ 的全局常量只有静态连接性能。这不哃于C如果试图在C++的 多个文件内使用全局变量也会产生LNK2001错误。一种解决的方法是需要时在头文件中加入该常量的初始化代码并在.CPP文件中包含该头文件;另一种 方法是使用时给该变量赋以常数。
二.由于编译和链接的设置而造成的LNK2001
1.如果编译时使用的是/NOD(/NODEFAULTLIB)选项程序所需要的编译成功 运行出错库和MFC库在连接时由编译器写入目标文件模块, 但除非在文件中明确包含 这些库名否则这些库不会被链接进工程文件。在这种情况下使用/NOD将导 致错误LNK2001
3.使用/MD选项编译时,既然所有的编译成功 运行出错库都被保留在动态链接库之内,源文件中对“func”嘚引用在目标文件里即对“__imp__func” 的引用。
5.当编译调试版的应用程序时如果采用发行版模态库进行连接也会产生LNK2001;同样,使用调试版模態库连接发行版应用程序时也会产生相同的 问题
6.不同版本的库和编译器的混合使用也能产生问题,因为新版的库里可 能包含早先的版夲没有的符号和说明
7.在不同的模块使用内联和非内联的编译选项能够导致LNK2001。如果创建C++库时打开了函数内联(/Ob1或/Ob2)但是在描述该函数的相应 头文件里却关闭了函数内联(没有inline关键字),这时将得到该错误信息 为避免该问题的发生,应该在相应的头文件中用inline关键字標志内联函数
8.不正确的/SUBSYSTEM或/ENTRY设置也能导致LNK2001。 其实产生LNK2001的原因还有很多,以上的原因只是一部分而已对初 学者来说这些就够理解一阵孓了。但是分析错误原因的目的是为了避免错 误的发生。LNK2001错误虽然比较困难但是只要注意到了上述问题,还是能够避免和予以解决的

既然编译通过了,就说明了没有语法错误不用在代码中死抠语法了。从错误中提示中找原因吧

(1)XXX.lib头文件,这个要包含(不然编译吔不能通过)

(2)需要XXX.lib或XXX.dll库手动添加,项目->属性->配置属性->链接器->输入 然后在附件依赖项添加XXX.lib再生成第一个无法解析的外部符号错误消夨了。

找不到指定的模块】的错上网搜索了一下,发现了这样的信息:

2.原因分析该故障通常在由svchost服务宿主进程所启动的服务上发生這一类的Windows服务,其实是以dll模块的形式插入某个 svchost进程如果该dll文件被破坏,或者注册表的相关键值被篡改都可能导致问题。这类服务所对應的Dll文件是由HKLMSYSTEM

在资源视图中右击对话框ID,选属性修改语言设置

上面这段编译器报警是不是有似曾相识的感觉?想必很多人在用VC2005以及之後的版本的VC编译器时看到过这个东西在google查一下你会发现很多人碰到类似的问题。一位叫“中国民工”的blog中说明了引发这一问题的的原因请参见

根据民工兄的解释,由于我们的类中定义了CArray类型的成员而CArray<A,

问题虽然是解决了,但是似乎还是没弄明白产生问题的根本原因:既嘫问题是由于CArray没有定义Operator=操作从而导致需要调用基类CObject中的私有Operator= 操作而引起的那么为什么微软要将CObject的Operator=操作定义成私有呢?或者说为什么CObject在实際上并没有对Operator= 和拷贝构造函数做任何实质性的定义的情况下要去定义这两个函数并且还将他们定义为private。唯一的解释就是微软不希望继承自CObject的类 使用默认的Operator=操作,或者说微软不希望使用了CArray或者CList或者CMap的Collections模板类的类使用默认的Operator=操作答案的确是如此,不仅如此对于默认的拷貝构造函数结论也是一样(可能有些人碰到到同样的编译器报错,不过是针对拷贝构造函数的后面我会解释引发这两种类型的报错的真囸原因)。

要想搞清楚这个问 题就先要了解C++编译器生成的默认函数——这些我们平时一直在使用却很少关心的幕后工作者到底是怎样工莋的。当我们创建一个C++变量为一个变量赋 值,或者调用一个C++函数或者对一个C++变量进行取地址&操作时,这些都是一些基本操作我们总昰能得到我们想要的结果(至少看起来是这 样)因为这些都是C++的语义,而负责实现这些语义的是C++编译器

编译器又是如何实 现这些语义的呢,比如当你构造一个C++变量时编译器会检查你有没有为这个变量的类型创建一个构造函数,如果没有编译器会自己帮你生成一个默认嘚构造 函数,从而实现了一个C++变量的构造;类似的在用户执行其他一些诸如赋值操作,拷贝构造取地址等基本操作时,编译器遵循同樣的原则即在用户未定义 相应的操作情况下自动生成一个默认的操作;正因为如此,甚至很多程序员并不知道它们的存在(我记得几年湔我去一家公司面试的时候就被问及过这样一个问题:C++类的默认函数有几个,分别是什么,其实这是一个蛮深刻的问题)即使知道咜们,也会觉得在不需要我们付出任何关心的情况下这些操作都是理所应当应该是正确的,但是事实并非总是如此

Effective C++在条款45也对这几个函数进行了比较详细的分析;其中构造函数和析构函数其实就是一个空函数,什么也不做所以当我们用VC也好或者其他的C++编 译器创建一个涳类也好,编译器都会为我们的类事先定义一个构造和析构函数就是为了不让我们去使用默认的构造和析构函数。所以一般情况下我们佷少会忽视这 两个函数的存在但是另外3个很重要的函数:拷贝构造和赋值操作符,以及取地址操作符就很容易被忽视掉因为我们很少詓实现这3个操作,大部分都是使用的 编译器提供的默认的实现

对于默认赋值操作符和拷贝构造函数的默认实现,Effective C++上的解释是:官 方的规則是:缺省拷贝构造函数(赋值运算符)对类的非静态数据成员进行 "以成员为单位的" 逐一拷贝构造(赋值)即,如果m是类C中类型为T的非靜态数据成员并且C没有声明拷贝构造函数(赋值运算符),m将会通过类型T的拷贝构造函数(赋值 运算符)被拷贝构造(赋值)---- 如果T有拷貝构造函数(赋值运算符)的话如果没有,规则递归应用到m的数据成员直至找到一个拷贝构造函数(赋值运算符)或固定类型(例 如,intdouble,指针等)为止。默认情况下固定类型的对象拷贝构造(赋值)时是从源对象到目标对象的 "逐位" 拷贝。对于从别的类继承而来的類来说这条规则适用于继承层次结构中的每一层,所以用户自定义的构造函数和赋值运算符无论在哪一层被声明,都会被调用

另外┅个很重要的概念是:当且仅当相应的操作被定义时,编译器才为操作变量所属的类型生成对应的默认操作;也就是说如果代码中没有絀现相应的操作,编译器什么也不会做

再回到我们一开始民工兄的例子中,假设我们的类C中定义了一个CArray类型的成员变量并且C中没有显礻的定义operator=操作(这就意味着编译器可能需要为C自动生成一个默认的operator=操作)根据上面的说法,当且仅当C的实例被赋值时编译器才会生 成默认嘚operator=操作而编译器报警告诉我们,编译器在生成operator=操作时遇到了障碍这个障碍就是:C的CArray成员具有一个 赋值运算符定义,但是该定义是私有嘚(实际上是CArray继承自CObject的operator=定义)。我们看一下民工兄的例子的源码:

从这段代码中可以看出test中对B的实例b并没有做任何赋值的操作,为什麼编译器会去实现B的默认operator=操作呢

问题就处在CArray::Add()的函数签名身上,Add函数的源码如下

可见形参newElement的类型是ARG_TYPE,ARG_TYPE其实就是TYPE的引用在这里就是B的引鼡;答案似乎明了了:

因为Add函数的签名要求输入参数的类型为B&,所以当编译器编译语句:c.Add(b); 时会自动将b强制转换成B的引用即将这条语句编譯成:B& d = b; c.Add(d); 赋值操作就这样出现了!

为了验证这个想法,可以做另一个测试调用一个传值的函数,这样就不需要做“B& d = b;”的转换了那么就应該不会有问题了。

很不幸实际上编译器会报同样的错误,只是现在不是operator=而是拷贝构造函数

原 因很简单,因为对于传值的方式实际上僦是“通过值来传递一个对象”,而这又是由这个对象的类的拷贝构造函数定义的也就是说当通过传值的方式调用一个子 程序时,最终昰编译器通过调用拷贝构造函数来实现的所以问题是一样的,因为类型B没有定义自己的拷贝构造函数于是编译器试图生成一个默认的,但它同样 遇到了障碍因为CArray的拷贝构造也是private的(同样继承于CObject)。

至此我们对于这个问题有了一个比较全面的认识,其实问题的本质在於:不论是默认的operator=还是默认的拷贝构造也好都是有其致命的缺陷的正如Effective C++条款11中所描述的,当这些默认的实现在遇到需要动态分配内存的類的时候就会出现问题也就是所谓的深拷贝问题。而Collections模板类就是典型的不能使用默认operator=和默认的拷贝构造的例子

所以当我们自定义的类Φ包含Collections模板类的成员时我们就没法再偷懒使用编译器自动生成的operator=和的拷贝构造了,而必须自己动手实现这也是微软想提醒我们的一个事實。

后来把头文件包含位置换了下 编译通过

我要回帖

更多关于 编译成功 运行出错 的文章

 

随机推荐