我一直认为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
部分,因为没有初始化。现在上面的文字已得到纠正。
答案 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型,以在您出生之前终止您的父母。
所有这些行为都是合法的(在遵循标准的意义上,是合法的 ),因此不对错误报告进行担保。