记一个C++中的疑难杂症
来源:互联网 发布:服务器php环境配置 编辑:程序博客网 时间:2024/05/17 04:55
最近在项目上遇到了一个C++中常见的疑难杂症。有些令人哭笑不得的感觉。先不多说,看下文。
有两个Project -- A 和 B. A会被编译链接成为一个dll文件,而B会生成一个exe文件。
在Project A中有一个类 C 和 一个类 D,如下:
在Project B中有一个类 X 及其成员函数 f(), 其定义大约如下:
具体现象如下:
因为上文已经将本问题极大地浓缩,相信聪明而经验丰富的你或许已经看出了问题所在。。。没看出来?!没关系,我们来debug一下:)不过实在要吐槽的是,本问题中的工程都是QT中的工程,而QT的Debugger虽然选的仍是微软的CDB,但是那个慢呀!按一下F10,至少要等半分钟才有反应,有时甚至好几分钟。所以不到万不得已,打log都比debug快得多。
在debug过程中,笔者发现,第2次进入f()以后,或者说第2次进入d_f2()后,仍然把d_f1()执行了,但是再接下去执行"m_value = 100;"的时候,程序就崩溃了。于是,问题来了:
1. 为什么第一次执行的时候不崩溃,而第二次就崩溃?
2. 为什么崩溃前还能把d_f1()执行了?
其实真正去做过debug就可以看出来,在访问到m_value的时候,已经显示对m_value的访问是非法的了!这就是崩溃的原因了。至于为何崩溃前仍能把d_f1()执行了,那是因为在d_f1()这个函数内并没有访问任何D的成员变量,而对于函数指针d_f1的访问,并没有涉及到访问其他人的内存。这就是第二个问题的答案了。
可是,第一个问题还没有回答,为何第一次没有崩溃呢?
(完)
有两个Project -- A 和 B. A会被编译链接成为一个dll文件,而B会生成一个exe文件。
在Project A中有一个类 C 和 一个类 D,如下:
class D {......public: void d_f1() { // do not access any member variables here } void d_f2() { d_f1(); m_value = 100; ...... }......private: int m_value; ......};class C {......public: C() { #ifdef __XXX__ p = new D; #endif } ~C() { #ifdef __XXX__ delete p; #endif } void c_f() { #ifdef __XXX__ p->d_f2(); #endif }......private: ...... #ifdef __XXX__ D* p; #endif ......};
在Project B中有一个类 X 及其成员函数 f(), 其定义大约如下:
class X {......public: X() { C m_var; }public: void f() { m_var.c_f(); int a = 100; ..... } ......private: C m_var; ......};
具体现象如下:
X x;x.f(); // 一切正常...x.f(); // 程序崩溃
因为上文已经将本问题极大地浓缩,相信聪明而经验丰富的你或许已经看出了问题所在。。。没看出来?!没关系,我们来debug一下:)不过实在要吐槽的是,本问题中的工程都是QT中的工程,而QT的Debugger虽然选的仍是微软的CDB,但是那个慢呀!按一下F10,至少要等半分钟才有反应,有时甚至好几分钟。所以不到万不得已,打log都比debug快得多。
在debug过程中,笔者发现,第2次进入f()以后,或者说第2次进入d_f2()后,仍然把d_f1()执行了,但是再接下去执行"m_value = 100;"的时候,程序就崩溃了。于是,问题来了:
1. 为什么第一次执行的时候不崩溃,而第二次就崩溃?
2. 为什么崩溃前还能把d_f1()执行了?
其实真正去做过debug就可以看出来,在访问到m_value的时候,已经显示对m_value的访问是非法的了!这就是崩溃的原因了。至于为何崩溃前仍能把d_f1()执行了,那是因为在d_f1()这个函数内并没有访问任何D的成员变量,而对于函数指针d_f1的访问,并没有涉及到访问其他人的内存。这就是第二个问题的答案了。
可是,第一个问题还没有回答,为何第一次没有崩溃呢?
因为第一次调用f()的时候,m_value这块内存是自己的;而第二次调用f()的时候,这块内存不是自己的了。
好吧,说了等于没说。为何第一次的时候是自己的,第二次就不是自己的了呢?
其实,Root Cause是在于,在Project A中是定义了宏 __XXX__ 的,而在Project B中并没有定义宏 __XXX__. 因为B.exe链接了A.dll,所以在第一次调用x.f()的时候,进入了A.dll的代码空间中,这里是存在宏 __XXX__,因此,对于 m_var 这个类 C 的实例,此时是认为其内存布局中是存在 D* p 的,于是便调用了 p->d_f2()。 此时因为是程序的早期,在d_f2()中对于 m_value 的访问并没有引发什么问题。(这里,笔者仍然是有疑问的:此时的d_f2()里的m_value所占用的内存空间到底是不是m_var的?似乎不是,但也没出错。)
但是,因为x这个实例是在栈上的,所以Project B中的 m_var 这个实例并不知道 __XXX__,所以 m_var 的内存空间中并没有 D* p. 而在第一次 m_var.f() 调用完成后,"int a = 100;" 等后续语句就会继续使用栈空间,即 int a = 100; 及其后续语句就会把原先存放 D* p 的栈空间占用掉,所以等到第二次执行x.f()时,即进入A.dll的代码空间时,会去访问不是 x 实例自己空间以试图访问 m_value,但因为那其实是其他实例(比如 int a)的空间,所以导致了程序崩溃。
修复的方法不言自明了, 只要在Project B中也加入宏 __XXX__ 即可。(完)
1 0
- 记一个C++中的疑难杂症
- 一个configure的疑难杂症
- 项目中的部分疑难杂症
- android编译中的疑难杂症
- vbox 使用中的疑难杂症
- MUI中的疑难杂症
- C 语言疑难杂症
- C语言指针疑难杂症
- WPF开发中的疑难杂症-控件设计篇-如何实现一个NumericBox
- 网页排版中的一些疑难杂症
- java学习中的疑难杂症(一)
- java学习中的疑难杂症(二)
- java学习中的疑难杂症(三)
- java学习中的疑难杂症(四)
- java学习中的疑难杂症(五)
- “疑难杂症”二记
- “疑难杂症”又二记
- 疑难杂症
- [转] 汉语骂人最高境界!(爆笑)
- bzoj 1095
- Android 学习笔记(4)—— ToggleButton 、Switch
- Android 使用 Gradle 多渠道打包
- UESTC 1299 Date 预处理、打表、找周期、前缀和
- 记一个C++中的疑难杂症
- HDU 5631 Rikka with Graph(无向图去边搜索)
- 给easyui tree树加图标
- java中IO类的各种操作
- 腾讯2016软开实习生笔试题-编程1:蛇形矩阵
- 项目1-分数的雏形
- 基于JSP在线答题程序设计
- shiro整合redis做缓存
- strcpy 字符串拷贝函数解析