34

我一直相信 GCC 会在 ELF 或此类文件的段(或用于优化的段)中放置一个static const变量。但似乎并非如此。.rodata.text

我目前正在gcc (GCC) 4.7.0 20120505 (prerelease)一台装有 GNU/Linux 的笔记本电脑上使用。它确实放置了一个静态常量变量来.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 是否有任何原因不能将 const 变量放入.rodata

更新:

经测试, GCCconst int a = 0;将放入一个具有显式初始化(​​如 )的常量变量,而我未初始化该变量。.rodata因此,这个问题可能会在以后关闭——我可能没有提出正确的问题。

另外,在我之前的话中,我写道变量 a 放在“.data”部分,这是不正确的。.bss由于未初始化,它实际上已放入部分中。上面的文字现在已更正。

4

4 回答 4

12

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

如果您希望它进入 rodata,请使用-fno-commonGCC 选项。

于 2012-05-30T15:53:32.773 回答
7

为什么GCC 会这样做?如果不问开发人员自己,就无法真正回答这个问题。如果允许我推测,我敢打赌它与优化有关——编译器不必强制执行 const。

也就是说,我认为最好还是看看语言本身,尤其是未定义的行为。有一些关于未定义行为的提及,但没有一个是深入的。

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

“但是如果我const_cast去掉 const 并修改 y 怎么办?” 然后你有未定义的行为。

未定义的行为 意味着编译器可以按字面意思做它想做的任何事情,并且无论编译器决定做什么都不会被视为违反 ISO 9899 标准。

3.4.3

1 未定义的行为

使用不可移植或错误程序结构或错误数据时的行为,本国际标准对此没有要求

2 注 可能的未定义行为范围从完全忽略具有不可预测结果的情况,到在翻译或程序执行期间以环境特征的记录方式表现(有或没有发出诊断消息),到终止翻译或执行(有发出诊断消息)。

ISO/IEC 9899:1999, §3.4.3

这意味着,因为您调用了未定义的行为,所以编译器所做的任何事情在技术上都是正确的,而不是正确的。因此,GCC 采取是正确的...

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 型号,以便在您出生之前终止您的父母。

所有这些行为都是合法的(嗯,在遵守标准的意义上是合法的),因此错误报告是没有保证的

于 2019-05-15T02:51:30.683 回答
3

写入已声明为const合格的对象是未定义的行为:任何事情都可能发生,即使那样。

在 C 中没有办法声明对象本身是不可变的,您只能通过您对它的特定访问权限来禁止它是可变的。在这里,您有一个int*, 因此修改是“允许的”,因为编译器不会被迫发出诊断。在 C 中进行强制转换意味着您应该知道自己在做什么。

于 2012-05-30T15:08:02.757 回答
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;
}
于 2012-05-30T15:19:12.497 回答