sta*_*ify 23 c memory gcc const readonly
我一直认为GCC会将static const变量放到ELF或此类文件的.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;
}
Run Code Online (Sandbox Code Playgroud)
那么,这是一个错误还是一个功能?我已经决定将此作为bug提交给bugzilla,但最好先请求帮助.
是否有任何理由说GCC不能放置const变量.rodata?
更新:
经测试,具有显式初始化(如const int a = 0;)的常量变量.rodata将由GCC 放置,而我将变量保持未初始化.因此,这个问题可能会在稍后结束 - 我可能没有提出正确的问题.
另外,在我之前的文字中,我写过变量a放在'.data'部分,这是不正确的.它实际上放入了.bss部分,因为没有初始化.现在上面的文字已得到纠正
编译器使它成为一个常见的,它可以与其他兼容的符号合并,并且如果最终没有明确初始化的定义,它可以进入bss(磁盘上没有空间).把它放在rodata中将是一种权衡; 你会在运行时节省内存(提交费用),但会在磁盘上占用更多空间(对于一个庞大的数组可能会占用很多空间).
如果您更愿意使用rodata,请使用-fno-commonGCC选项.
为什么GCC这样做?如果不询问开发人员自己,就无法真正回答这个问题。如果我允许猜测,我打赌它与优化做的-编译器不具有强制执行常量。
也就是说,我认为最好看看语言本身,尤其是未定义的行为。有一些提到未定义的行为,但没有一个深入。
修改常量是未定义的行为。Const 是一个契约,在 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;
Run Code Online (Sandbox Code Playgroud)
...并把它变成一个.rodata符号,同时采取...
static const int a; // guaranteed to be zero
Run Code Online (Sandbox Code Playgroud)
...并把它变成一个.bss符号。
在前一种情况下,任何修改尝试a——即使是通过代理——通常都会导致分段违规,从而导致内核强制终止正在运行的程序。在后一种情况下,程序可能会运行而不会崩溃。
也就是说,猜测编译器会做哪一个是不合理的。Const 是一个契约,你,程序员,通过不修改应该是常量的数据来维护这个契约。违反该契约意味着未定义的行为,以及随之而来的所有可移植性问题和程序错误。
所以 GCC 可以做一些事情。
它可能会将符号写入 .rodata,在操作系统内核下为其提供保护
它可能会将对象写入不能保证内存保护的地方,在这种情况下......
它可能会改变价值
它可能会更改该值并立即将其改回
它可能会完全删除有问题的代码,理由是值没有改变(0 -> 0),本质上是优化......
int main(){
int *p = &a;
*p = 0;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
...到...
int main(void){
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它甚至可能会在您出生之前及时发送一个模型 T-800 以终止您的父母。
所有这些行为都是合法的(好吧,在遵守标准的意义上是合法的),因此错误报告没有保证。
写入已声明为const合格的对象是未定义的行为:任何事情都可能发生,即使是这样。
C 中没有办法声明对象本身是不可变的,只能通过对它的特定访问来禁止它是可变的。这里有一个int*,因此修改是“允许”的,因为编译器不会被迫发出诊断。在 C 中进行强制转换意味着您应该知道自己在做什么。