GCC:为什么常量变量没有放在.rodata中

时间:2012-05-30 14:51:40

标签: c memory gcc const readonly

我一直认为GCC会将static const个变量放到ELF或此类文件的.rodata段(或.text段进行优化)。但似乎并非如此。

我目前正在使用GNU / Linux的笔记本电脑上使用gcc (GCC) 4.7.0 20120505 (prerelease)。它确实将一个静态常量变量放到.bss段:

/*
 * this is a.c, and in its generated asm file a.s, the following line gives:
 *   .comm a,4,4 
 * which would place variable a in .bss but not .rodata(or .text)
 */
static const int a;

int main()
{
    int *p = (int*)&a;
    *p = 0;  /* since a is in .data, write access to that region */
             /* won't trigger an exception */
    return 0;
}

那么,这是一个错误还是一个功能?我决定将此作为bug提交给bugzilla,但最好先请求帮助。

是否有任何理由说GCC无法在.rodata中放置const变量?

更新:

经测试,具有显式初始化的常量变量(如const int a = 0;)将由GCC放入.rodata,而我将变量保留为未初始化。因此,这个问题可能会在稍后结束 - 我可能没有提出正确的问题。

另外,在我之前的文字中,我写过变量a放在'.data'部分,这是不正确的。它实际上放在.bss部分,因为没有初始化。现在上面的文字已得到纠正。

4 个答案:

答案 0 :(得分:9)

编译器使它成为一个常见的,它可以与其他兼容的符号合并,并且如果最终没有明确初始化的定义,它可以进入bss(在磁盘上不占用空间)。把它放在rodata中将是一种权衡;你会在运行时节省内存(提交费用),但会在磁盘上占用更多空间(对于一个巨大的数组可能会占用很多空间)。

如果您更愿意使用rodata,请使用-fno-common选项加入GCC。

答案 1 :(得分:2)

写入已声明为const限定的对象是未定义的行为:任何事情都可能发生,即便如此。

C中没有办法声明对象本身是不可变的,你只能禁止它通过你拥有的特定访问来变化。这里有一个int*,因此编译器不会被强制发出诊断,因此修改是“允许的”。用C语言进行演员表示你想知道自己在做什么。

答案 2 :(得分:1)

  

是否有任何原因导致GCC无法在.rodata中放置const变量?

您的程序由编译器优化(即使在-O0完成了一些优化)。常量传播完成:http://en.wikipedia.org/wiki/Constant_folding

尝试像这样欺骗编译器(请注意,此程序仍然是技术上未定义的行为):

#include <stdio.h>

static const int a;

int main(void)
{
    *(int *) &a = printf("");  // compiler cannot assume it is 0

    printf("%d\n", a);

    return 0;
}

答案 3 :(得分:1)

为什么要使用 GCC ?如果不问开发人员自己,就无法真正回答这个问题。如果允许我推测,我认为它与优化有关–编译器不必必须强制执行const。

也就是说,我认为最好看看语言本身,尤其是未定义的行为。 很少提到未定义的行为,但是没有一个深入。

修改常量未定义的行为。 Const is a contract,在C(和C ++)中尤其如此。

  

“但是如果我const_cast离开const并仍然修改y怎么办?”然后,您会有未定义的行为。

未定义行为 的意思是,允许编译器按其字面意思做任何事情,并且编译器决定做的任何事情都不会被视为违反了ISO 9899标准。

  

3.4.3

     

1种不确定的行为

     

使用非便携式或错误程序构造或错误数据时的行为,本国际标准对此不施加任何要求

     

2注释可能的不确定行为包括:完全忽略具有无法预测的结果的情况,在翻译或程序执行过程中以环境特征的书面方式记录的行为(有无诊断消息),终止翻译或执行以下操作:执行(带有诊断消息的发布)。

     

ISO / IEC 9899:1999,§3.4.3

这意味着,因为您已经调用了未定义的行为,所以编译器所做的任何操作在技术上都是正确的,而 not 不正确。因此,海湾合作委员会采取...是正确的。

static const int a = 0;

...同时将其变成.rodata符号,同时......

static const int a; // guaranteed to be zero

...并将其变成.bss符号。

在前一种情况下,任何尝试a的修改(即使通过代理进行修改)通常都会导致分段违规,从而导致内核强制杀死正在运行的程序。在后一种情况下,程序可能会运行而不会崩溃。

也就是说,猜测编译器将执行哪一个是不合理的。 Const是一种合同,程序员(您自己)可以通过不修改应该为常数的数据来维护该合同。违反该合同意味着未定义的行为,以及随之而来的所有可移植性问题和程序错误。

GCC可以做几件事。

可能将符号写入.rodata,以在操作系统内核下提供保护

可能将对象写入无法保证内存保护的位置,在这种情况下...

可能会更改值

可能会更改值并立即将其更改回

它可能会在值不变的情况下完全删除有问题的代码(0 -> 0,从本质上进行优化...

int main(){
    int *p = &a;
    *p = 0;
    return 0;
}

...到...

int main(void){
    return 0;
}

它甚至可能会及时发回T-800型,以在您出生之前终止您的父母。

所有这些行为都是合法的(在遵循标准的意义上,是合法的 ),因此不对错误报告进行担保。