是否允许在c++中命名全局变量' read '或' malloc ' ?



考虑以下c++ 17代码:

#include <iostream>
int read;
int main(){
std::ios_base::sync_with_stdio(false);
std::cin >> read;
}

它在GCC 11.2和Clang 12.0.1的Godbolt上编译和运行良好,但如果使用-static键编译则会导致运行时错误。

据我所知,有一个POSIX(?)函数叫做read(参见man read(2)),所以上面的例子实际上调用了ODR冲突,即使在没有-static的情况下编译,程序本质上也是病态的。如果我尝试将变量命名为malloc, GCC甚至会发出警告:built-in function 'malloc' declared as non-function

上面的程序是有效的c++ 17吗?如果没有,为什么?如果是,它是一个编译器错误,阻止它运行?

显示的代码是有效的(我相信所有的c++标准版本)。类似的限制都列在[reserved.names]。由于read没有在c++标准库中声明,也没有在C标准库中声明,也没有在旧版本的标准库中声明,并且没有在那里列出,因此它可以作为全局命名空间中的名称。

所以它是一个实现缺陷,它不会与-static链接?(不是"编译器bug")-工具链的编译器部分很好,并且没有什么禁止对有效代码发出警告。)它至少可以在默认设置下工作(尽管因为GNU链接器不介意在动态库的未使用对象中复制符号),并且有人可能会认为这就是标准遵从所需要的全部。

我们还有at [intro.compliance]/8

一个符合标准的实现可以有扩展(包括额外的库函数),只要它们不改变任何格式良好的程序的行为。实现需要根据本国际标准诊断使用这种病态扩展的程序。然而,在这样做之后,他们可以编译和执行这些程序。

可以将POSIX函数视为这样的扩展。在何时或如何启用这些扩展方面故意含糊不清。GCC工具集的g++驱动程序默认链接了许多库,我们可以认为这不仅增加了非标准#include头文件的可用性,而且还为程序添加了额外的翻译单元。从理论上讲,g++驱动程序的不同参数可以使它在没有使用libc.so的底层链接步骤的情况下工作。但是祝你好运——有人可能会说,没有一种简单的方法可以只链接c++和C标准库中的名称而不包括其他非保留名称,这是一个问题。

(不修改一个格式良好的程序甚至意味着实现扩展不能为额外的库使用非保留名称吗?我希望不是,但我能看到一个严格的阅读暗示。

所以我还没有对这个问题给出一个明确的答案,但是实际情况是不可能改变的,在我看来,标准缺陷报告比一个有用的澄清更吹毛求疵。

下面是关于为什么只使用-static会产生运行时错误的一些解释。

问题中的https://godbolt.org/z/asKsv95G5链接表示-static的运行时错误是Program returned: 139。Linux下Bash中kill -l的输出包含11) SIGSEGV(且128 + 11 = 139),因此进程退出时带有致命信号SIGSEGV (Segmentation fault),提示无效内存引用。其原因是进程试图将read变量的内容(4字节)作为机器码运行。(最终std::cin >> ...调用read。)要么是这4个字节中的某些东西意外地解释为机器码失败,要么是因为包含这4个字节的内存页不可执行而失败。

在没有-static的情况下成功的原因是,使用动态链接可以有多个具有相同名称的符号(read):一个在程序可执行文件中,另一个在共享库中(libc.so.6)。std::cin >> ...(inlibstdc++.so.6)指向libc.so的链接。6,所以当动态链接器试图在程序加载时找到符号read时(libstdc++.so.6使用),它将查看libc.so。首先,在可执行程序中找到read,并忽略read符号。

最新更新