Gcc 使用符号'_结束';在g++;导致分割错误 考虑下面的C++源代码: int_end[1050]; int main(){ 对于(int i=0;i

Gcc 使用符号'_结束';在g++;导致分割错误 考虑下面的C++源代码: int_end[1050]; int main(){ 对于(int i=0;i,gcc,linker,clang,Gcc,Linker,Clang,编译行:g++main.cpp-o main-O0 在Ubuntu 14.04下使用gcc-4.8.4和clang-3.6.0时,运行此代码会导致分段错误。奇怪的行为是符号\u end指向静态分配数组\u end的末尾,而不是其开头。如果我们将\u end替换为end,一切正常 此外,如果我们要求gcc通过提供-S命令行参数来输出汇编代码,则带有“_end”的版本与带有任何其他数组名称的版本之间不会有显著差异: $ g++ main.cpp -o main.s -O0 -S $ g++ main

编译行:
g++main.cpp-o main-O0

在Ubuntu 14.04下使用gcc-4.8.4和clang-3.6.0时,运行此代码会导致分段错误。奇怪的行为是符号
\u end
指向静态分配数组
\u end
的末尾,而不是其开头。如果我们将
\u end
替换为
end
,一切正常

此外,如果我们要求gcc通过提供-S命令行参数来输出汇编代码,则带有“_end”的版本与带有任何其他数组名称的版本之间不会有显著差异:

$ g++ main.cpp -o main.s -O0 -S
$ g++ main2.cpp -o main2.s -O0 -S
$ diff main.s main2.s
1,2c1,2
<   .file   "main.cpp"
<   .globl  _end
---
>   .file   "main2.cpp"
>   .globl  end_
5,7c5,7
<   .type   _end, @object
<   .size   _end, 4200
< _end:
---
>   .type   end_, @object
>   .size   end_, 4200
> end_:
25c25
<   movl    $0, _end(,%rax,4)
---
>   movl    $0, end_(,%rax,4)

据我所知,gcc编译器可能会根据需要处理以下划线开头的变量,即。E在代码中使用此类符号是一种糟糕的做法。但我的问题是:这里到底发生了什么?为什么将
\u end
替换为已分配数组末尾的地址?为什么使用“-S”命令行参数没有区别,但在创建的二进制文件中却有区别?在这种情况下,gcc和clang的行为并不完全相同,这对我来说也很奇怪。

\u
开头的令牌是保留的,您不应该使用它们。似乎
\u end
是为Linux上编译的程序定义的一个外部符号,表示未初始化数据段(也称为BSS段)末尾之后的第一个地址

注意:在某些系统中,这些符号的名称前面有 下划线,因此:_etext、_edata和_end

来源:

7.1.3“保留标识符”表示:

所有以下划线开头的标识符始终保留为在普通和标记名称空间中用作具有文件范围的标识符

那么我们必须知道:

  • 文件作用域用于全局(其他为函数和块作用域)
  • 普通名称空间包含变量
因此,根据C99,您不能使用标识符
\u end

您需要实施

现在,要了解它在您的实现中实际失败的原因,请使用:

g++ -Wl,--verbose main.c
查看使用的链接器脚本

在Ubuntu 15.10上,它在数据部分的末尾定义了符号
\u end

_end = .; PROVIDE (end = .);
. = DATA_SEGMENT_END (.);

因此,难怪提前访问内存可能会出错。

正是我所需要的,谢谢!但是为什么“-S”命令行参数在编译此代码时没有显示任何可疑信息?@MaximAkhmedov这可能是因为
\u end
与任何其他指针一样是指针,并且在分配给数组时会执行指针算术。
_end = .; PROVIDE (end = .);
. = DATA_SEGMENT_END (.);