【问题标题】:Is it allowed to name a global variable `read` or `malloc` in C++?是否允许在 C++ 中命名全局变量“read”或“malloc”?
【发布时间】:2021-11-24 04:37:39
【问题描述】:

考虑以下 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 键编译会导致运行时错误。

据我了解,有一个名为 read 的 POSIX(?) 函数(请参阅 man read(2)),因此上面的示例实际上调用了 ODR 违规,即使在没有 @987654327 的情况下编译该程序也基本上是格式错误的@。如果我尝试命名变量malloc:built-in function 'malloc' declared as non-function

,GCC 甚至会发出警告

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

【问题讨论】:

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

标签: c++ gcc language-lawyer posix clang++


【解决方案1】:

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

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

我们也有[intro.compliance]/8

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

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

(不改变格式良好的程序是否意味着实现扩展不能为其他库使用非保留名称?我希望不会,但我可以看到一个严格的阅读暗示。)

因此,我没有对这个问题提出明确的答案,但实际情况不太可能改变,在我看来,标准缺陷报告比有用的澄清更挑剔。

【讨论】:

  • GLIBC manual 说:“所有来自 ISO C 标准的库类型、宏、变量和函数的名称都是无条件保留的;你的程序可能不会重新定义这些名称。”
  • 另外,reserved.names-3 似乎也这么说。
  • @Ruslan: 是的,但请注意read 不是 C 标准函数(不像,例如,fread - 或 malloc,来吧)。
  • @psmears 确实,没想到。
  • 相关:如果你想使用你自己的函数malloc,你还需要GCC选项-fno-builtin-malloc来移除它作为__builtin_malloc的别名的隐含定义。 (这是 GCC 能够内联 memcpy 的机制,或者让 malloc 知道返回的指针没有任何别名,并且是对齐的:What improvements does GCC's `__builtin_malloc()` provide over plain `malloc()`?。)@Ruslan。这通常与不链接 glibc 的内核相关,有些使用 -fno-builtin 禁用所有内容。
【解决方案2】:

这里解释了为什么它只使用-static 会产生运行时错误。

问题中的https://godbolt.org/z/asKsv95G5 链接表示-static 的运行时错误是Program returned: 139。在 Linux 上的 Bash 中 kill -l 的输出包含 11) SIGSEGV(和 128 + 11 = 139),因此进程以致命信号 SIGSEGV(分段错误)indicating无效内存引用退出。原因是进程试图将 read 变量的内容(4 个字节)作为机器代码运行。 (最终std::cin &gt;&gt; ... 调用read。)这 4 个字节中的某些错误被意外解释为机器代码,或者因为包含这 4 个字节的内存页不可执行而失败。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-15
    • 2019-05-05
    • 1970-01-01
    • 1970-01-01
    • 2021-03-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多