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

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

提问人:yeputons 提问时间:10/3/2021 最后编辑:yeputons 更新时间:10/4/2021 访问量:3186

问:

请考虑以下 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(?) 函数叫做(参见 man read(2)),所以上面的例子实际上调用了 ODR 冲突,即使编译时没有 .如果我尝试命名一个变量,GCC 甚至会发出警告:read-staticmallocbuilt-in function 'malloc' declared as non-function

上面的程序是否有效 C++17?如果不是,为什么?如果是,是编译器错误阻止了它运行吗?

C GCC 语言律师 POSIX CLANG++

评论

2赞 yeputons 10/3/2021
@Someprogrammerdude,实际上,这就是最初问题的来源。随机C++谜语的重要来源。
2赞 yeputons 10/3/2021
@Someprogrammerdude,是否需要很大程度上取决于比赛。例如,许多俄罗斯当地的ICPC比赛都有非常严格的时间限制,你最好不要使用,因为它很慢。显然,它是大型 I/O 和此类比赛中使用的特定编译器/默认编译标志的组合。sync_with_stdio(0)<iostream>
3赞 Evg 10/3/2021
不幸的是,这也是误解和完全糟糕的代码示例的重要来源。这些网站对C++初学者造成的伤害是巨大的。这与专业编程无关。
2赞 yeputons 10/3/2021
@Evg 是的,有点。但是,在这种情况下,需要此行才能出现问题。这本身并不违法,因此有问题。
6赞 Eljay 10/3/2021
全局命名空间是狂野的西部。如果上述程序没有违反 ODR,则为 C++17 有效。ODR 是由于您提供的代码还是由于平台提供的代码而无关紧要。

答:

17赞 aschepler 10/3/2021 #1

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

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

我们也有 [intro.compliance]/8

符合要求的实现可以具有扩展(包括附加库函数),前提是它们不会改变任何格式良好的程序的行为。根据本国际标准,需要实现来诊断使用格式错误的此类扩展的程序。但是,这样做后,他们可以编译和执行此类程序。

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

(不更改格式正确的程序是否意味着实现扩展不能对其他库使用非保留名称?我希望不是,但我可以看到一个严格的解读暗示了这一点。

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

评论

6赞 Ruslan 10/4/2021
GLIBC manual says: "The names of all library types, macros, variables and functions that come from the ISO C standard are reserved unconditionally; your program may not redefine these names."
1赞 Ruslan 10/4/2021
Also, reserved.names-3 seems to say the same.
2赞 psmears 10/4/2021
@Ruslan: True, but note that is not a C standard function (unlike, for example, - or , come to that).readfreadmalloc
0赞 Ruslan 10/4/2021
@psmears indeed, didn't think of it.
0赞 Peter Cordes 10/4/2021
Related: If you want to use your own function called , you also need the GCC option to remove the implicit definition of it as an alias for the . (That's the mechanism by which GCC is able to inline memcpy, or for malloc to know that the returned pointer doesn't alias anything, and is aligned: What improvements does GCC's `__builtin_malloc()` provide over plain `malloc()`?.) @Ruslan. This is normally relevant in kernels, which don't link glibc, and some use to disable everything.malloc-fno-builtin-malloc__builtin_malloc-fno-builtin
6赞 pts 10/4/2021 #2

Here is some explanation on why it produces a runtime error with only.-static

The https://godbolt.org/z/asKsv95G5 link in the question indicates that the runtime error with is . The output of in Bash on Linux contains (and 128 + 11 = 139), so the process exits with fatal signal SIGSEGV (Segmentation fault) indicating invalid memory reference. The reason for that is that the process tries to run the contents (4 bytes) of the read variable as machine code. (Eventually calls read.) Either somethings fails in those 4 bytes accidentally interpreted as machine code, or it fails because the memory page containing those 4 bytes is not executable.-staticProgram returned: 139kill -l11) SIGSEGVstd::cin >> ...

The reason why it succeeds without is that with dynamic linking it's possible to have multiple symbols with the same name (read): one in the program executable, and another one in the shared library (libc.so.6). (in libstdc++.so.6) links against libc.so.6, so when the dynamic linker tries to find the symbol read at program load time (to be used by libstdc++.so.6), it will look at libc.so.6 first, finding read there, and ignoring the read symbol in the program executable.-staticstd::cin >> ...