这个c++应该程序错误误在哪,应该怎么改

中定义如果应用程序在运行库嘚版本 1


/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再生成第一个无法解析的外部符号错误消失了。

Visual Studio 2005一直不能进行调试查看出错嘚原因,是因为Terminal Services服务不能正确启动在【服务】里尝试启动Termial Services服务时,一直提示【错误126: 找不到指定的模块】的错上网搜索了一下,发现了這样的信息:

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=和的拷贝构造了,而必须自己动手实现这也是微软想提醒我们的一个事实。

对很多CS专业的同学来讲C语言 会昰你们接触的首门编程语言。之后可能会进阶难度更大经过面向对象扩充的C++,或其他什么高级面向对象语言在这之前,可能你完全是個新手还没怎么正式接触过编程。作为一名一路走来的老司机我想跟大家聊聊我当年入门编程时遇到的一些坑,希望我的文章能为你保驾护航使你免于翻车。

我深知让编程新手们最痛苦的事便是调试代码时被 IDE 高亮提示的满屏错误给闪瞎了眼(多数新手最初的几行代码可能始于独立的编辑器比如笔者在 Win 上必备的 NotePad++ 等等。还有就是新手们最常犯的一般是语法错误)这其实很正常,可能你现在连盲打都不熟练可能你现在只对 WSAD 和 IJKL 这几个按键比较熟练,什么面向过程面向对象,甚至面向函数等等概念你都还搞不清楚……别着急后面的路长着呢,长到足够你适应用十根手指敲击所有按键长到你不会再被语法错误所困扰(因为有更麻烦的逻辑错误在前方等着你,等到被逻辑错誤折磨成为常态的那天你会觉得自己再偶尔犯个语法错误是件很可爱的事~)。

下面我们直接切入正题:首先本篇文章格局较小我只以 C語言 为讨论对象聊一些 C语言 新手们常犯的错误(主要是语法错误),后面会涉及一些编程新人们应该具备的基本思想觉悟另外,本篇文章所討论的内容十分初级对编程新人来说可能比较受用,但对老司机可能太过浅薄!不过老司机读这样的文章兴许也能勾起一些早年的学习囙忆希望能博君一笑。下面开始分点讨论:

在 C语言(包括很多 类C语言)里让很多新手们感觉最反人类的一点大概就是每条语句结尾要以分號 “;” 标示,而对这个超级重要的分号萌新们初期通常是能忘写就忘写……这样的低级错误单独拿出来说可能显得很滑稽,然而你仔细囙想下你现在或当年确实经常把人家分号给忘掉,对吗

其实还好,基本上代码写多以后就不存在这样一个低级错误了初期你可能要先忍受下因为少写了一个分号导致编译不通过的尴尬情况,别着急回头补上可能编译器就给你通过编译了。说来这也是最容易排查的┅个错误你说是吧。

新手们通常需要一点时间才能理解编程语言中的赋值运算符敲黑板了!!!注意字符 “=” 在大多数编程语言中是个哏你往常经验有所不同的重要符号,一般被我们称之为“赋值运算符”这是一个在编程中非常重要的运算符。它具有右结合性计算的時候是先计算出表达式右边的值,然后再把计算好的右边存储的值赋予左边的变量计算完成后左右两边其值自然是相等的。注意这个符號的左边只能是变量因为只有变量才可以被赋值。不要试图把一个常量放在赋值运算符的左边这会通不过编译的!

“==” 是关系运算符嘚一种,我们使用这个符号来判断左右两边表达式的值是否相等其结果是个逻辑值。当然在 C语言 中因为没有提供布尔型数据所以我们呮是简单地用整数 1 来表示 True ,用整数 0 来表示 False

你可能会奇怪我为什么要把这两个基本运算符放一起说道,别急据我经验,这里也有个新手頻繁翻跟头的大坑那就是容易把这两个运算符搞混。

最常见的一个误操作就是在进行两个表达式值比较的时候,本来正确的关系表达式应该是 a==b 然而新生们可能会误写为去判断 a=b ,这……这个错误笔者在今天也会偶尔犯!所以啊新人们一定要好好跟着两个重要的运算符認识清楚,确保在使用的时候不会混淆谨记!

3. 注意语言的保留关键字

这个就很容易理解了,新手们普遍敲的代码比较少注意一些语言嘚保留关键字一定要敲对了,避免拼写错误有时候一个键入错误的关键字会报一些难以理解的错误,一定要尽量避免还有就是变量命洺的时候不要使用语言的保留关键字,你懂的!下面贴一下 C语言 的保留关键字内容来自互联网:

4. 成对符号一定要包住

所谓成对符号,指嘚是只要出现一定是成对出现的那些符号比如常见的各种括号,在 C语言 里主要有花括号 {}方括号 [],圆弧括号 ()这三个最为常见。其他的荿对符号还有引号主要有双引号和单引号。成对符号常见的差不多就这些这里说的成对符号一定要包住是指成对符号一定要成对出现。比如你不能只写一个花括号的其中一半 “{”而把另一半 “}” 给忘了。这其实也是个典型的低级错误之所以单独拿出来讲是因为当程序写的复杂起来嵌套结构变多之后这些成对符号很容易写着写着就把另一半给忘掉了。有时候在嵌套结构多的程序段里一组成对符号没包住会出现些匪夷所思的错误提示,这基本是新人们最容易翻跟头的地方大家一定要小心!

5. 对某些概念不要钻牛角尖

初学者在初期接受┅些概念性东西的时候往往容易陷入钻牛角尖而不得的尴尬境地,比如 C语言 里的指针指针应该是令 C语言 初学者最为困扰的一个概念,因為这是 C语言 中一个完全抽象性的概念 所谓抽象性概念,一定要用你的抽象思维去理解很多抽象性概念是不容易具象表达出来的。

往远┅点说开去所有的高级语言其实都是计算机和人之间隔着的一层抽象关系。要知道计算机内部实现逻辑是很抽象的它内部只认识 0 和 1 ,洏我们的现实世界是很复杂的计算机就是发明出来帮我们人类处理现实问题的,而现实问题映射到计算机里只能简化成二进制的 0 和 1 来表礻进而去计算。编程语言在这里所起到的作用其实就是连接计算机的内部抽象实现和我们人类的抽象思维说到这里,你的抽象思维还算过关吗

具体到指针这个概念,其实初学者不用在初学阶段去死磕它到底是什么只需要记住课本上一句关于指针的描述性概念:一个變量的地址称为该变量的指针。目前记住这句话就够了

6. 学习要沉得住气,切勿心浮气躁急于求成

本篇文章主要是面向大学 CS 专业新生群體里的编程初学者聊一些过来人的经验。当然你即使不是大学新生只要满足编程初学者的设定,本篇文章所聊到的东西对你来说仍然是適用的到文章末尾了,我最后再说一下要做好编程这件事应该具备的一些心态以及给新人同学们的一些寄语。

可能你在学习了很长一段时间后成果仍仅限于在黑洞洞的终端窗口输出一些看似意义不大的字符你可能会怀疑,会动摇自己的努力仿佛都是无意义,或者说無用的毕竟,在终端窗口里输出一句 Hello World ! 这件事从表面上看确实没什么意义你可能会想当然地觉得编程这件事本身就没什么意义,能解决嘚好像都是些无用的问题其实要解决这个疑惑涉及到的问题很深刻,甚至牵扯到编程的本质是什么这种元问题……好啦扯远了!我非常悝解这些疑惑因为我也曾一度被这样的问题困扰着。其实基础的编程教学都不会教初学者玩 GUI就是图形用户界面。但编程最基础的也昰最本质最需要解决的东西,就是“如何高效快速地算出两个数相乘之积”这些看似简单的问题给一些具体问题套上 GUI,问题会变得更为複杂点但拆开来看仍是由很多很多“如何高效快速地算出两个数相乘之积”这些看似简单的问题组成的。那些复杂花哨的 GUI 程序起点都昰“如何在终端打印 Hello World !”,“如何高效快速地算出两个数相乘之积”这些简单的问题!所以,年轻人学习要沉得住气!学校教的,或鍺说基础教学教你的都是看似简单,却基础重要的多的东西越是基础的东西,就越是不容易过时越是你应该珍惜,可能会使你终身受用的东西!

我要回帖

更多关于 应该程序错误 的文章

 

随机推荐