是否允许在 C++ 中命名全局变量“read”或“malloc”?

Is it allowed to name a global variable `read` or `malloc` in C++?

考虑以下 C++17 代码:

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

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

据我了解,有一个名为read的POSIX(?)函数(见man read(2)),所以上面的例子实际上调用了ODR违规,程序是即使在没有 -static 的情况下编译,本质上也是病式的。如果我尝试将变量命名为 mallocbuilt-in function 'malloc' declared as non-function

,GCC 甚至会发出警告

上面的程序是否有效 C++17?如果没有,为什么?如果是,是否是编译器错误导致它无法 运行?

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

所以它不会 link 和 -static 是一个实现缺陷吗? (不是“编译器错误” - 工具链的编译器部分很好,并且没有什么禁止对有效代码发出警告。)它至少可以使用默认设置(尽管因为 GNU linker 不不要介意动态库中未使用的 object 中的重复符号),有人可能会争辩说这就是标准合规性所需要的。

我们在 [intro.compliance]/8

也有

A conforming implementation may have extensions (including additional library functions), provided they do not alter the behavior of any well-formed program. Implementations are required to diagnose programs that use such extensions that are ill-formed according to this International Standard. Having done so, however, they can compile and execute such programs.

我们可以考虑POSIX这样的功能扩展。关于何时或如何启用此类扩展,这是有意含糊的。 GCC 工具集的 g++ driver 默认情况下 link 有许多库,我们可以认为它不仅增加了 non-standard #include headers 的可用性而且还向程序添加了额外的翻译单元。理论上,g++ driver 的不同参数可能会使其在没有使用 libc.so 的基础 link 步骤的情况下工作。但祝你好运 - 有人可能会争辩说,没有简单的方法可以 link 只使用 C++ 和 C 标准库中的名称而不包括其他未保留的名称。

(不改变 well-formed 程序甚至意味着实现扩展不能使用 non-reserved 名称作为额外的库吗?我希望不会,但我可以看到一个严格的阅读暗示.)

所以我还没有对这个问题提出明确的答案,但实际情况不太可能改变,我认为标准缺陷报告 nit-picking 比有用的澄清更重要。

这里有一些关于为什么它只在 -static 时产生 运行 时间错误的解释。

https://godbolt.org/z/asKsv95G5 link in the question indicates that the runtime error with -static is Program returned: 139. The output of kill -l in Bash on Linux contains 11) SIGSEGV (and 128 + 11 = 139), so the process exits with fatal signal SIGSEGV (Segmentation fault) indicating无效内存引用。原因是该进程试图 运行 读取 变量的内容(4 个字节)作为机器代码。 (最终 std::cin >> ... 调用 read。)这 4 个字节中的某些内容被意外解释为机器代码,或者失败是因为包含这 4 个字节的内存页不可执行。

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