gcc初始化顺序问题

【问题】

    最近在封装一个底层库的时候遇到诡异的问题,用gcc4.1.2编译的程序一切正常,

    用gcc4.8.2编译的程序运行总是coredump。 经过分析后发现是初始化顺序问题,代码如下(经过简化):

 

    typedef std::map<std::string, int> typeid_t;

    typeid_t TYPEID; 

 

    __attribute__((constructor)) void init()

    {

        TYPEID["http"] = 2;

    }

 

    原因:之所以出现coredump,是因为在执行init函数的时候变量TYPEID还根本没有初始化!

 

【问题代码背景】

    一个底层库需要做一些初始化操作,这个初始化需要隐式地自动地进行,需要对上层使用者透明,

    而且需要在程序进入main函数之前自动初始化完成。

 

【解决方法】

    想到的第一个解决方法:用指针而不是全局变量,规避初始化顺序的问题:

    typedef std::map<std::string, int> typeid_t;

    typeid_t* TYPEID; 

    __attribute__((constructor)) void init()

    {

             TYPEID = new typeid_t;

        (*TYPEID)["http"] = 2;

    }

 

    这种方法可以解决问题,但是怎么看怎么别扭,总觉得不够上流,而且TYPEID引用起来也麻烦。

    试着将constructor属性用在变量上面,结果编译错误了。开始回忆,记忆中可以给全局变量指定

    初始化优先级,只是记不清那个属性的名字,于是翻了一下gcc手册,找到init_priority属性可以

    用来干这个事情。理论上只要给init_priority指定一个比constructor更小的数字就行了(数字越

    小,优先级越高)

 

    试验第二种方法:

    __attribute__((init_priority(1))) std::map<std::string, int> TYPEID; 

    __attribute__((constructor(2))) void init()

    {

        TYPEID["http"] = 2;

    }

 

    用gcc4.8.2编译正常,但是用gcc4.1.2编译不正常。于是翻了一下gcc4.1.2的手册,发现gcc4.1.2

    的constructor属性根本不支持参数! :(

 

    问题貌似搞不定呢能否在不给constructor传递参数的前提下解决这个问题呢?如果不给constructor

    传递参数,constructor有默认参数吗?有的话"默认的参数应该是多少呢?

 

    在回答这个问题之前,先看看优先级的数字范围。init_priorityconstructor的参数用以指定变量或者函数

    的初始化优先级,这个参数是一个unsigned short整数范围是[0, 65535], 但是gcc不让用0,所以真正可以

    使用的数字范围是[1, 65535]。注意数字越小,优先级越高。

 

    回到之前那个问题,经过测试后,发现constructor不传递参数(gcc4.1.2压根儿传不了)的时候确实是有默认参

    数的,而且这个默认参数就是区间的最大值65535. 有了这个依据,问题就好办了,只需要给TYPEID

    init_priority属性传递一个小于65535的整数就行了代码如下:

 

    typedef std::map<std::string, int> typeid_t;

    __attribute__((init_priority(65534))) typeid_t TYPEID; 

    __attribute__((constructor)) void init()

    {

        TYPEID["http"] = 2;

    }

 

至此,以上代码在gcc4.1.2gcc4.8.2都可以正常运行了!

 

【总结】

    为了确保 '全局变量/对象 在 __attribute__((constructor))函数 之前初始化', 需要给全局变量加上初始化优先级,

    另外gcc4.1.2constructor不支持参数,为了gcc4.1.2/gcc4.8.2都能正确工作,需要这样:

    __attribute__((init_priority(65534))) std::map<std::string, int> TYPEID; 

    __attribute__((constructor)) void init()

    {

        TYPEID["http"] = 2;

    }    

    1. constructor的默认优先级是65535,属性值区间是[1,65535], [1, 100]这个区间的值也可以,

      但是编译有警告,因为[1, 100]是留给gcc内部使用的,所以最好使用[101, 65535]这个区间。

    2. 上面init_priority的参数为65534,实际上可以是[101,65534]内的任意一个值(gcc4.1.2即便传65535

       一样可以,但是gcc4.8.2不行,所以为了兼顾gcc的2个版本,必须要小于65535)

    3. gcc4.1.2编译:全局变量/对象 默认在__attribute__((constructor))函数之""初始化,

       如果是gcc4.1.2,则根本不需要给TYPEID指定init_priority属性。保证始终在constructor函数前执行。

    4. gcc4.8.2编译:全局变量/对象 默认在__attribute__((constructor))函数之""初始化


你可能感兴趣的:(gcc初始化顺序问题)