Andy Niu �����ĵ�

Andy Niu

Andy Niu Help  1.0.0.0
CPP常见错误

变量

 系统类型fd_set重复定义
 
 Stack_overflow
 
 NULL指针初始化string
 
 模拟CPP程序崩溃
 
 无法从char指针转换为string引用
 
 不是类或命名空间名称
 
 error_LNK2005
 
 vector_subscript_out_of_range
 
 无法打开文件vld_lib
 
 已经在LIBCMTD_lib中定义
 
 踩内存导致delete的时候崩溃
 
 无法定位程序输入点
 
 无法解析的外部符号
 
 无法打开文件LIBC_lib问题
 
 VS调试报错:不是内部或外部命令,也不是可运行的程序或批处理文件
 
 vector重复定义
 
 野指针可能不会立即崩溃,会导致未定义行为
 

详细描述

变量说明

error_LNK2005
1、错误原因:链接的时候,发现重复定义。
2、比如:在a.cpp和b.cpp中都定义方法如下:
    int doSth(int a)
    {
        return a+1;
    }
3、编译报错,b.obj : error LNK2005: "int __cdecl doSth(int)" (?doSth@@YAHH@Z) 已经在 a.obj 中定义
    链接的时候,发现b.obj中的doSth方法已经在a.obj中定义,重复定义。
NULL指针初始化string
1、报错:
    terminate called after throwing an instance of 'std::logic_error' what(): basic_string::_S_construct NULL not valid
2、错误原因:对一个空指针进行操作。如下:
    char* p = NULL:
    string str(p);  //运行时报错
参见
Stack_overflow
    错误原因:栈上分配了太多的内存,栈的内存一般只有1M。
    错误代码:
    char tmp[1024*1024*10];
参见
vector_subscript_out_of_range
1、错误原因,vector下标越界。
2、代码如下:
    int main(int argc, char* argv[])
    {
        vector<int> intVec;
        intVec.push_back(1);
        int ff = intVec[1];
    
        getchar();
    }
3、VS弹出错误窗口,Debug Assertion Failed!
    这时候,点击重试,触发一个断点,点击中断,在调用堆栈即可找到错误的代码行。
    注意:VS弹出错误窗口,千万不能点击中止,否则的话,程序直接关闭,看不到错误的代码行。
4、另外有几个单词,需要注意一下:
    script      脚本
    subscript   下标
    scribe      抄写员
    subscribe   订阅
vector重复定义
1、测试代码如下:
    a.h
    #ifndef A_H_
    #define A_H_
    #include "b.h"
    class A
    {
    public:
        int _Id;
        B   _B;
    };
    #endif
    
    b.h
    #ifndef B_H_
    #define B_H_
    #include <vector>
    using namespace std;
    vector<int> aVec;
    class B
    {
    public:
        int _Id;
    };
    #endif
    也就是说,A包含B的头文件,而在B的头文件中定义了一个aVec;
    同时还有a.cpp、b.pp以及main.cpp
    a.cpp
    #include "a.h"
    
    b.cpp
    #include "b.h"
    
    main.cpp
    #include "a.h"
    int main(int argc, char* argv[])
    {
        A a;
        return 0;
    }
2、编译报错如下:
    1>a.obj : error LNK2005: "class std::vector<int,class std::allocator<int> > aVec" (?aVec@@3V?$vector@HV?$allocator@H@std@@@std@@A) 已经在 main.obj 中定义
    1>b.obj : error LNK2005: "class std::vector<int,class std::allocator<int> > aVec" (?aVec@@3V?$vector@HV?$allocator@H@std@@@std@@A) 已经在 main.obj 中定义
    1>D:\Temp\NiuzbRepository\trunk\C++\CppTest\Debug\TestStatic.exe : fatal error LNK1169: 找到一个或多个多重定义的符号
3、错误原因,vs已经说的很清楚,就是aVec重复定义了。
    原因是:在头文件定义aVec,被多个编译单元包含,每个编译单元都一个aVec的定义,重复定义。
    链接的时候,每个编译单元都是外部链接,这么多aVec,不知道该使用了哪个aVec。
4、表面一看,上面报错的错误比较奇怪,一大堆信息,不容易看清楚,要仔细读一遍,才能找出关键信息,aVec重复定义。
    我们换成一个int就很清楚了。如下:
    b.h
    #ifndef B_H_
    #define B_H_
    int hhh;
    class B
    {
    public:
        int _Id;
    };
    #endif
    报的错误如下:
    1>a.obj : error LNK2005: "int hhh" (?hhh@@3HA) 已经在 main.obj 中定义
    1>b.obj : error LNK2005: "int hhh" (?hhh@@3HA) 已经在 main.obj 中定义
    1>D:\Temp\NiuzbRepository\trunk\C++\CppTest\Debug\TestStatic.exe : fatal error LNK1169: 找到一个或多个多重定义的符号
VS调试报错:不是内部或外部命令,也不是可运行的程序或批处理文件
1、错误原因是什么?
    我们运行一个程序,会引用其它的库文件或者调用其它的命令,必须告诉操作系统,这个程序所依赖的库文件和命令在哪个地方。
    如果没有告诉操作系统,对于引用的库文件,报错:无法启动此程序,因为计算机中丢失xxx.dll
    对于调用的命令(也就是执行其它的程序),报错:不是内部或外部命令,也不是可运行的程序或批处理文件
2、怎么解决这个问题:
    必须告诉操作系统,所依赖的库文件和命令在哪个地方。windows下就是path
    VS调试的时候,在工程属性-->调试-->环境,增加 path=../common;c:/windows/system32; 
    使用批处理运行,在开头增加 path=../common;%path%;
    注意:在linux下使用 export LD_LIBRARY_PATH=../common;
3、在构建工程的时候,对于编译时引用的头文件和连接时引用的lib文件,是同样的道理,必须告诉头文件和lib文件在哪个目录。
    头文件的设置在: 在工程属性-->C/C++-->常规-->附加包含目录
    lib文件的设置在:在工程属性-->链接器-->常规-->附加库目录
不是类或命名空间名称
1、今天遇到一个奇怪的问题,使用一个类,并且引用了对应的头文件,但是奇怪的是,
    报错error C2653: "ibpUtils": 不是类或命名空间名称。
2、仔细思考,为什么?
    include "xxx.h" 就是对xxx.h进行文本替换。这就存在问题,重复引用同一个头文件,会导致头文件中的内容重复定义。
    谁会很傻地重复引用同一个头文件呢?如下:
    include "xxx.h" 
    include "xxx.h" 
    这种很傻瓜的错误当然可以避免,但是有一种情况,却很难避免,间接地导致重复引用同一个头文件。比如:
    a.h 引用头文件 b.h
    c.h 引用头文件 a.h和b.h,由于c.h无法知道a.h已经引用了b.h,因此会
    include "a.h"
    include "b.h"
    编译器对include会递归地进行替换,也就是先替换a.h,对于a.h中的include "b.h" 再次替换,
    这就导致了b.h的内容重复出现在c.h中。
3、怎么解决这个问题?
    使用头文件保护符,如下:
    #ifndef _XXX_H_
    #define _XXX_H_
    ......
    #endif
    这就保证了不会重复包含同一个头文件。
4、但是,有一点需要特别注意。那就是:头文件保护符不能有重复的宏,如果出现重复的宏,就会导致下一个头文件不能被包含进来。
5、现在问题有思路了,既然明明包含了头文件,却找不到对应的类或命名空间,肯定是存在头文件保护符相同,导致了没有被包含进来。
6、解决办法:ibpUtils不存在,找到ibpUtils对应的头文件,搜索头文件保护符,果然有重复的头文件保护符
参见
已经在LIBCMTD_lib中定义
1、错误信息如下:
    1>MSVCRTD.lib(MSVCR80D.dll) : error LNK2005: _printf 已经在 LIBCMTD.lib(printf.obj) 中定义
2、错误原因:
    使用不同的库,而这些库使用不同的运行时库,在这些运行时库中,对同一个方法都有实现,导致这些方法重复定义。
3、解决办法:
    使用相同的运行时库。在属性-->C/C++-->代码生成-->运行时库
    libcmt对应多线程的静态库
    msvcr80对应多线程的动态库
    其中d表示debug版本。   
4、还有一种解决办法:
    忽略某一个特定的库,这种办法可能存在问题,不建议使用。
无法从char指针转换为string引用
1、代码如下:
    char tmp[64];
    sprintf(tmp,"Andy");

    string aa = tmp;    
    string& bb = tmp;
    const string& cc = tmp;
2、其中string& bb = tmp; 报错 error C2440: “初始化”: 无法从“char [64]”转换为“std::string &”
3、思考为什么?
4、string aa = tmp; 左边string会调用构造方法,右边是char*,string存在构造方法接收char*,因此会调用string的构造方法。
    上面的语句等价于 string aa(tmp);
5、string& bb = tmp; 左边是string的引用,期望接收string对象,但是右边不是string对象,这个时候要产生一个中间对象,
    也就是临时对象,但是这个临时对象是const对象,const对象不能赋值给非const的引用。
    这就能解释为什么const string& cc = tmp; 是正确的。
6、那么为什么中间的临时对象是const对象呢?
    假定中间的临时对象不是const对象,string& bb = tmp 是正确的,那么,程序员操作bb="hello",
    期望会修改tmp的值,我们知道这是不可能的,因为修改的是临时对象,与tmp完全不搭嘎。
    也就是说,中间的临时对象不是const对象,会导致与程序员期望不一致的结果,
    为了避免这个问题,中间的临时对象是const对象。
7、注意:string aa = tmp; 存在隐式类型转化,直接调用对应的构造方法,但是不存在中间的临时对象。
参见
无法定位程序输入点
1、错误原因:动态加载的时候无法找到VTDU_A_SetResMsgCallBack方法的实现。
无法打开文件LIBC_lib问题
1、错误原因:
    如果将用低版本的VC开发的项目,拿到高版本的VC开发环境上去编译,链接时也许会触发LNK1104错误,如下:
    1>LINK : fatal error LNK1104: 无法打开文件“LIBC.lib”
2、解决办法:链接时忽略此库,在此提供两种解决方案:
    Project | Properties | Configuration Properties | Linker | Input
    Ignore Specific Library: libc.lib
    或
    #pragma comment(linker, "/NODEFAULTLIB:libc.lib")
无法打开文件vld_lib
fatal error LNK1104: 无法打开文件vld.lib
1、今天发现一个很奇怪的问题,具体的场景是:
    cmu_sdk(动态库)---> ibp_utils(动态库)---> vld(动态库)
2、现在修改ibp_utils,不依赖vld,重新编译ibp_utils和cmu_sdk,结果发现:
    cmu_sdk链接的时候报错:fatal error LNK1104: 无法打开文件 vld.lib
3、错误原因是:cmu_sdk还在试图去连接vld.lib
4、这是为什么呢?
    思考,cmu_sdk去连接的库,包含两部分:直接依赖的库和间接依赖的库
    间接依赖的库也就是,依赖一个静态库,而这个静态库又依赖其它的库,也就是说,当前库间接依赖其它库。
5、仔细查找,果然发现cmu_sdk(动态库)--->new_ibp_protocol(静态库)--->ibp_utils(动态库)---> vld(动态库)
6、重新编译new_ibp_protocol静态库,再次编译cmu_sdk,结果OK
无法解析的外部符号
1、错误原因:链接的时候,找不到方法的实现。
2、但是整个解决方案都搜索不到 SetModuleName
3、仔细分析,对于当前类的方法,只有声明,没有定义,如果没有调用方法,不会报错。
4、但是对于继承,比如 VruMaster继承了ibpMaster。
    ibpMaster声明了虚方法,没有定义,即使没有调用方法,子类构建,也会链接失败。
5、因此,可以断定VruMaster 引用的头文件有SetModuleName 这个虚方法的声明。
模拟CPP程序崩溃
1、在项目中经常遇到C++程序崩溃,这些崩溃都是有原因的,往往是因为自己对C++使用不当造成的,模拟C++崩溃的场景。
2、除0,如下:
    int main(int argc, char* argv[])
    {
        return 1/0;
    }
    编译:
    [root@localhost dump]# g++ -o main main.cpp 
    main.cpp: In function ‘int main(int, char**)’:
    main.cpp:3: warning: division by zero in ‘1 / 0’
    编译已经给出警告
    [root@localhost dump]# ./main
    Floating point exception
    没有生成core文件,原因是没有设置ulimit -c,ulimit -c表示程序错误,把程序在内存中的信息转存到文件。如下:
    [root@localhost dump]# ulimit -c
    0
    [root@localhost dump]# ulimit -c 1000
    [root@localhost dump]# ./main
    Floating point exception (core dumped)
    [root@localhost dump]# ll
    total 112
    -rw------- 1 root root 196608 Jan 28 17:53 core.27550
    -rwxr-xr-x 1 root root   4863 Jan 28 17:49 main
    -rwxrwxrwx 1 root root     50 Jan 28 17:46 main.cpp
    dump转存的时候,如果程序在内存的大小,超过-c的设置,就不会产生dump文件,因此要把-c设置很大,最好不限制。如下:
    [root@localhost dump]# ulimit -c unlimited
3、上面已经是很短的崩溃程序,能不能更短呢?
    int main=0;
    崩溃原因:程序会试图按照调用方法的方式去执行main,这当然不行。
4、栈溢出(stack overflow)
    #include <stdio.h>
    int main(int argc, char* argv)
    {
        char pc[1024*1024*1024*2];
        getchar();
        return 0;
    }
    编译报错,如下:
    [root@localhost dump]# g++ -o main main.cpp
    main.cpp: In function ‘int main(int, char*)’:
    main.cpp:4: error: overflow in constant expression
    main.cpp:4: error: size of array ‘pc’ is negative
    编译器很聪明,因为栈的大小一般为1M,这里是2M,直接编译报错。需要糊弄一下编译器,如下:
    #include <stdio.h>
    int main(int argc, char* argv)
    {
        int size = 1024*1024*1024*2;
        char pc[size];
        getchar();
        return 0;
    }
5、踩内存
    #include <string.h>
    int main(int argc, char* argv)
    {
        int a =10;
        char pc[4] = {0};
        strcpy(pc,"aaaaaaaaaaaaaaa");
        a = a+10;
        return 0;
    }
    或者
    #include <memory.h>
    int main(int argc, char* argv)
    {
        int a =10;
        char pc[4] = {0};
        char* pc2 = "aaaaaaaaaaaaaaa";
        memcpy(pc,pc2,strlen(pc2));
        a = a+10;
        return 0;
    }
系统类型fd_set重复定义
1、今天遇到一个很奇怪的问题,如下:
    error C2011: “fd_set”: “struct”类型重定义
2、发现是因为包含某个头文件导致的。调整一下头文件的包含顺序,发现就正常了。为什么呢?
3、include头文件,本质上就是文本替换(替换的过程中,会根据宏进行条件判断,是否包含,是否编译)
    不同的include顺序会导致不同的结果。
4、举例来说:
    a.h
    #ifndef A_H_
    #define A_H_
    struct AAA
    {
    
    };
    #endif
    
    b.h
    #ifndef B_H_
    #define B_H_
    #ifndef A_H_
    struct AAA
    {
    
    };
    #endif
    #endif
5、考虑a.h与b.h不同的include顺序,a,b顺序没有问题,b,a导致AAA重复定义。
参见
踩内存导致delete的时候崩溃
1、今天遇到一个奇怪的问题,delete的时候程序崩溃。new出来的内存,只是释放一次,并没有多次释放。
2、思考为什么?
    delete崩溃,说明要释放的堆内存已经损坏。损坏的原因有很多,比如踩内存。比如:
    char* aaa = new char[4];
    sprintf(aaa,"1111111111");
    delete []aaa;
野指针可能不会立即崩溃,会导致未定义行为
1、测试代码
    #include <string>
    using namespace std;
    
    class Person
    {
    public:
        int    _Age;
        string _Name;
    };
    
    int main()
    {
        Person* p = new Person();
        p->_Age = 18;
        p->_Name = "Andy";
        
        delete p;
        p->_Age = 20;
        p->_Name = "Bill";
    
        return 0;
    }
2、delete p; 之后,p是野指针,
    p->_Age = 20; 不会导致崩溃
    而p->_Name = "Bill"; 会导致崩溃,崩溃信息如下:
    NzbUtils.exe 中的 0x57b6d5a8 (msvcr80d.dll) 处未处理的异常: 0xC0000005: 写入位置 0xfeeefeee 时发生访问冲突
3、这是因为在VS的Debug模式,delete p之后,内存信息都是 0x fe ee fe ee
4、也就是说,对于野指针的操作,比如p->_Age = 20; 不会导致崩溃,但是会产生未定义的行为,很难定位到问题。
5、怎么解决这个问题?
    delete p之后,马上赋值为NULL,再对p操作,马上就崩溃。崩溃如下:
    NzbUtils.exe 中的 0x00411ce0 处未处理的异常: 0xC0000005: 写入位置 0x00000000 时发生访问冲突
6、以上是在VS测试的,在linux使用g++测试,对于野指针的操作,
    delete p;
    p->_Age = 20;
    p->_Name = "Bill";
    都不会崩溃
7、因此,野指针的问题非常严重,会导致不确定的行为。不知道什么时候崩溃,以及在哪里崩溃。
    一定要避免野指针的问题,也就是delete之后,马上赋值为NULL
Copyright (c) 2015~2016, Andy Niu @All rights reserved. By Andy Niu Edit.