第一次在Linux下导出类库的惨痛经历
2015-02-12 12:02
106 查看
项目工程中,准备对一个基础的类进行封装,封装成共享库(动态/静态)是不错的选择。
之前在Windows都是照猫画虎的搞一下,没有什么深刻的印象。但是这次在Linux下的类的导出真是刻骨铭心:各种灵异现象,各种悲剧,各种内存出错!
特来分享一下,与大家共勉~ 如果直接想看结论,调到文章最后好了,就一句话。
好吧,一个非常灵异,但其实是很二的故事,黑贝狗~
一个非常简单的类:
真的是什么也没做,然后在不明真相的情况下使用这个类,神奇的事情就发生了:
1. 如果new了CTest,
那么在delete的时候就会内存出错,各种严厉、奇怪的告警。
其实可以看到一些端倪:此处CTest new出来的对象的成员地址是非常奇怪的。
2. 那么如果直接声明一个对象呢?
貌似没什么问题,但其实是有问题的,而且要命!
测试代码:
测试结果:
触目惊心的内存访问越界!但是我自认为什么也没干啊,只不过是声明了一个CTest对象而已啊!
意识到问题的严重之后,我重新把类加回到主程序之中,一切又恢复原样了……
把凌乱的情况总结一下:
1. 我定义了一个类,使用共享库的方式导出;
2. 使用这个库,无论是直接声明对象,还是new一个,都是出现了内存的错误!
3. 拿回到主程序,一切正常。
这个情况让我一度怀疑gcc啊,类的导出机制啊这些我根本就不懂的东西,觉得人生如此凶险。
好吧,谜题揭晓吧,是一个非常二的地方——我在库的导出时操作都是OK的,但是使用库的时候,我自作聪明地“为了隐藏尽可能多的细节”,供外部使用的头文件中去除了所有的私有成员!
是的,这是我犯二时候使用的库的导出头文件,没有错,就是案发现场:
当意识到问题出在这里之后,答案貌似就很简单了——使用库的时候,根本不知道你这个对象需要分配多大的内存空间!
使用new的时候,首个成员在栈上,其余在堆上;直接声明时,首个成员OK,但是其余成员则是被安排到了其他的地址段了,覆盖了其他的变量。
说了这么多,其实都是我在异常情况下的困顿经历。
血的教训就一句话:类在供外部使用时,其成员不应该隐藏,否则会直接影响内存空间的分配。
之前在Windows都是照猫画虎的搞一下,没有什么深刻的印象。但是这次在Linux下的类的导出真是刻骨铭心:各种灵异现象,各种悲剧,各种内存出错!
特来分享一下,与大家共勉~ 如果直接想看结论,调到文章最后好了,就一句话。
好吧,一个非常灵异,但其实是很二的故事,黑贝狗~
一个非常简单的类:
class CTest { public: CTest( int a ); ~CTest(); private: int a; int b; int c; int d; };
CTest :: CTest( int a ) { a = 0; b = 0; c = 0; d = 0; printf( "[%s]********************\n", __func__ ); printf( "a Addr: %p\n", &a ); printf( "b Addr: %p\n", &b ); printf( "c Addr: %p\n", &c ); printf( "d Addr: %p\n", &d ); }
真的是什么也没做,然后在不明真相的情况下使用这个类,神奇的事情就发生了:
1. 如果new了CTest,
printf( "call new CTest" ); CTest *pTest = new CTest( 1 ); assert( NULL != pTest ); if ( NULL != pTest ) { delete pTest; }
那么在delete的时候就会内存出错,各种严厉、奇怪的告警。
call new CTest[CTest]******************** a Addr: 0xbfad1dc4 b Addr: 0x902000c c Addr: 0x9020010 d Addr: 0x9020014 [~CTest]******************** *** glibc detected *** ./main: free(): invalid next size (fast): 0x09020008 *** ======= Backtrace: ========= /lib/libc.so.6[0x4fb394] /lib/libc.so.6(cfree+0x96)[0x4fd346] /usr/lib/libstdc++.so.6(_ZdlPv+0x21)[0x7182591] ./main[0x8048c26] ./main[0x8048d26] /lib/libc.so.6(__libc_start_main+0xe5)[0x4a26d5] ./main[0x80488b1]
其实可以看到一些端倪:此处CTest new出来的对象的成员地址是非常奇怪的。
2. 那么如果直接声明一个对象呢?
貌似没什么问题,但其实是有问题的,而且要命!
测试代码:
void func1( void ) { char array[ARRARY_SIZE]; memset( array, 0x5A, ARRARY_SIZE ); for (int i = 0; i < ARRARY_SIZE; i++) { printf( "array[%02d]: %02d, Addr: %p\n", i, array[i], &(array[i]) ); } CTest test( 1 ); #if 0 printf( "call new CTest" ); CTest *pTest = new CTest( 1 ); assert( NULL != pTest ); if ( NULL != pTest ) { delete pTest; } #endif for (int i = 0; i < ARRARY_SIZE; i++) { printf( "array[%02d]: %02d, Addr: %p\n", i, array[i], &(array[i]) ); } }
测试结果:
array[00]: 90, Addr: 0xbf8fabf4 array[01]: 90, Addr: 0xbf8fabf5 array[02]: 90, Addr: 0xbf8fabf6 array[03]: 90, Addr: 0xbf8fabf7 array[04]: 90, Addr: 0xbf8fabf8 array[05]: 90, Addr: 0xbf8fabf9 array[06]: 90, Addr: 0xbf8fabfa array[07]: 90, Addr: 0xbf8fabfb array[08]: 90, Addr: 0xbf8fabfc array[09]: 90, Addr: 0xbf8fabfd array[10]: 90, Addr: 0xbf8fabfe array[11]: 90, Addr: 0xbf8fabff array[12]: 90, Addr: 0xbf8fac00 array[13]: 90, Addr: 0xbf8fac01 array[14]: 90, Addr: 0xbf8fac02 array[15]: 90, Addr: 0xbf8fac03 array[16]: 90, Addr: 0xbf8fac04 array[17]: 90, Addr: 0xbf8fac05 array[18]: 90, Addr: 0xbf8fac06 array[19]: 90, Addr: 0xbf8fac07 [CTest]******************** a Addr: 0xbf8fabd4 b Addr: 0xbf8fabf7 c Addr: 0xbf8fabfb d Addr: 0xbf8fabff array[00]: 90, Addr: 0xbf8fabf4 array[01]: 90, Addr: 0xbf8fabf5 array[02]: 90, Addr: 0xbf8fabf6 array[03]: 00, Addr: 0xbf8fabf7 array[04]: 00, Addr: 0xbf8fabf8 array[05]: 00, Addr: 0xbf8fabf9 array[06]: 00, Addr: 0xbf8fabfa array[07]: 00, Addr: 0xbf8fabfb array[08]: 00, Addr: 0xbf8fabfc array[09]: 00, Addr: 0xbf8fabfd array[10]: 00, Addr: 0xbf8fabfe array[11]: 00, Addr: 0xbf8fabff array[12]: 00, Addr: 0xbf8fac00 array[13]: 00, Addr: 0xbf8fac01 array[14]: 00, Addr: 0xbf8fac02 array[15]: 90, Addr: 0xbf8fac03 array[16]: 90, Addr: 0xbf8fac04 array[17]: 90, Addr: 0xbf8fac05 array[18]: 90, Addr: 0xbf8fac06 array[19]: 90, Addr: 0xbf8fac07
触目惊心的内存访问越界!但是我自认为什么也没干啊,只不过是声明了一个CTest对象而已啊!
意识到问题的严重之后,我重新把类加回到主程序之中,一切又恢复原样了……
把凌乱的情况总结一下:
1. 我定义了一个类,使用共享库的方式导出;
2. 使用这个库,无论是直接声明对象,还是new一个,都是出现了内存的错误!
3. 拿回到主程序,一切正常。
这个情况让我一度怀疑gcc啊,类的导出机制啊这些我根本就不懂的东西,觉得人生如此凶险。
好吧,谜题揭晓吧,是一个非常二的地方——我在库的导出时操作都是OK的,但是使用库的时候,我自作聪明地“为了隐藏尽可能多的细节”,供外部使用的头文件中去除了所有的私有成员!
是的,这是我犯二时候使用的库的导出头文件,没有错,就是案发现场:
class CTest { public: CTest( int a ); ~CTest(); };
当意识到问题出在这里之后,答案貌似就很简单了——使用库的时候,根本不知道你这个对象需要分配多大的内存空间!
使用new的时候,首个成员在栈上,其余在堆上;直接声明时,首个成员OK,但是其余成员则是被安排到了其他的地址段了,覆盖了其他的变量。
说了这么多,其实都是我在异常情况下的困顿经历。
血的教训就一句话:类在供外部使用时,其成员不应该隐藏,否则会直接影响内存空间的分配。
相关文章推荐
- 2017 BAT某家实习生电面---第一次电面的惨痛经历
- LINUX下安装QT的惨痛经历
- LINUX下安装QT的惨痛经历
- 追忆:第一次成立技术论坛的经历
- 用DELL的一次DEBUG惨痛经历(两天啊)
- 导出Linux系统调用表(sys_call_table)
- 我的第一次应聘经历有感--书到用时方恨少
- 生意场上惨痛经历的骗术总结
- 第一次去GF家的经历
- Linux那些事儿之我是U盘(26)第一次亲密接触(二)
- 今天我经历了惊心的第一次!!!
- 这是我第一次发BLOG,写点个人经历
- Linux那些事儿之我是U盘(28)第一次亲密接触(四)
- 惨痛的学车经历
- Linux那些事儿之我是U盘(27)第一次亲密接触(三)
- 从 window上的 SqlServer导出数据到 linux 上的Sybase
- 最近一次折腾LINUX的经历(1)。
- 在ASP.net中,不引用第三方类库的、轻便的、将报表导出到Excel中的方法
- 导出C++中的类到Lua脚本中的经历
- 第一次网管经历