您的位置:首页 > 运维架构 > Linux

第一次在Linux下导出类库的惨痛经历

2015-02-12 12:02 106 查看
项目工程中,准备对一个基础的类进行封装,封装成共享库(动态/静态)是不错的选择。

之前在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,但是其余成员则是被安排到了其他的地址段了,覆盖了其他的变量。



说了这么多,其实都是我在异常情况下的困顿经历。

血的教训就一句话:类在供外部使用时,其成员不应该隐藏,否则会直接影响内存空间的分配
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: