为啥 const int main = 195 会导致程序正常工作,但没有 const 会导致分段错误?

Posted

技术标签:

【中文标题】为啥 const int main = 195 会导致程序正常工作,但没有 const 会导致分段错误?【英文标题】:Why does const int main = 195 result in a working program but without the const it ends in a segmentation fault?为什么 const int main = 195 会导致程序正常工作,但没有 const 会导致分段错误? 【发布时间】:2016-01-23 04:20:35 【问题描述】:

考虑以下 C 程序(参见现场演示 here)。

const int main = 195;

我知道在现实世界中没有程序员编写这样的代码,因为它没有任何用处,也没有任何意义。但是,当我从程序上方删除const 关键字时,它会立即生成segmentation fault。为什么?我很想知道这背后的原因。

GCC 4.8.2 在编译时给出以下警告。

警告:'main' 通常是一个函数 [-Wmain]

const int main = 195;
          ^

为什么const 关键字的存在和不存在会对程序的行为产生影响?

【问题讨论】:

根据标准,这只是未定义的行为。 @machine_1 195 是 8086 及其后继操作码 ret(从函数返回)的编码。你可以猜到当你把它放在一个变量中并将该变量作为函数调用时会发生什么。 链接到How can a program with a global variable called main instead of a main function work?可能相关 您是否故意选择了与ret指令一致的值? @Ruslan 如果您进行一些搜索,您可以在多个地方找到此版本的各种版本。在堆栈交换网络this was one of the older references。在我对上面链接的回答中,我们可以找到一个 1984 年的 IOCCC 条目,它做了类似的事情,但要复杂得多。 【参考方案1】:

观察值 195 如何对应于 8086 兼容机上的 ret(从函数返回)指令。因此,main 的这个定义就像你在执行时将它定义为 int main() 一样。

在某些平台上,const 数据被加载到可执行但不可写的内存区域,而可变数据(即未限定 const 的数据)被加载到可写但不可执行的内存区域。出于这个原因,当您将 main 声明为 const 时,程序“工作”,但当您离开 const 限定符时,程序“工作”。

传统上,二进制文件包含三个段:

text 段(如果架构支持)是写保护和可执行的,包含可执行代码、static 存储持续时间限定的变量const 和字符串文字 data 段是可写的,不能执行。它包含未限定 const 且具有 static 存储持续时间的变量和(在运行时)具有 已分配 存储持续时间的对象 bss 段类似于data 段,但初始化为全零。它包含 static 存储持续时间未限定 const 的变量,这些变量已在没有初始化程序的情况下声明 stack 段不存在于二进制文件中,并且包含具有自动存储持续时间的变量

从变量main 中删除const 限定符会导致它从text 移动到不可执行的data 段,从而导致您观察到分段违规。

现代平台通常有更多细分(例如,rodata 细分既不可写也不可执行的数据),因此请不要在未查阅特定于平台的文档的情况下将其视为对您平台的准确描述。

请理解,不将main 设为函数通常是不正确的,尽管从技术上讲,平台可以允许将main 声明为变量,参见。 ISO 9899:2011 §5.1.2.2.1 ¶1,强调我的:

1 程序启动时调用的函数名为main。实现没有声明这个函数的原型。它应定义为返回类型int,并且没有参数(...)或两个参数(...)或等效项;或以其他实现定义的方式。

【讨论】:

一些优点,在​​ cmets 中 zwol touched on some of these here 我对这个问题的类似 C++ 版本的回答 请在您的答案中包含您对操作码 ret 编码的评论。这是理解所描述行为的关键。 @user19474 这样更好? @FUZxxl:很好的答案。但是你的回答没有解释为什么程序以垃圾值作为返回状态而不是 0 退出?如果能说出背后的原因就更好了。 @PravasiMeet ret 指令退出当前函数。它不设置返回值。因此程序退出时,eax 寄存器中的任何内容在 main 返回时,即随机垃圾值。【参考方案2】:

在 C 中,全局范围内的main 几乎总是一个函数。

在全局范围内使用main 作为变量会使程序的行为未定义。

(只是可能在您编写const 时,编译器会将变量优化为常量,因此您的程序行为不同。但程序行为仍然是 未定义)。

【讨论】:

不。平台可能允许“以实现定义的方式”声明main 我太老了,无法浏览标准,但我想它必须总是是一个函数! 参见。 ISO 9899:2011 §5.1.2.2.1 “程序启动时调用的函数名为 main。实现没有声明这个函数的原型。它应定义为返回类型为 int 且不带参数: (...) 或带有两个参数(此处称为 argc 和 argv,尽管可以使用任何名称,因为它们对于它们所在的函数是本地的)已声明): (...) 或等价物; 10)或以其他一些实现定义的方式。” 好的。很好的标准参考!我已经修改了。

以上是关于为啥 const int main = 195 会导致程序正常工作,但没有 const 会导致分段错误?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 main 前面有一个 int ,为啥我的教授会排除它? [复制]

以这种格式将 argv 变量传递给 main 的结果 main( int argc, char const * argv )

int main(int, char const* const*) 格式正确吗?

为啥在 cpp 文件中定义了非 int const 静态变量?

用常量参数定义 main (const int argc, const char * const argv[])?

为啥我们不能定义一个指向 int 指针的低 + *** const 指针?