停止由 C++ 包含引起的 <stdio.h> 定义的误捕

问题描述

在对 another answer评论中,我看到了一个 code example,它似乎使用了 printfputs,但没有包含 <stdio.h>,但在线编译器没有抱怨。[1]为了了解发生了什么,我将其复制到我的本地 IDE。

减少到相关的包含和输出,基本上是这样的:

#include <string>
#include <map>
#include <optional>

int main() {
  printf("Answer %d\n",42);
  puts("Question?");
}

使用 gcc 8.1.0(与 Clode::Blocks 20.03 一起打包)进行实验,我发现包含可以进一步减少到

  • <string><map><optional> 在 C++17 (ISO/GCC) 中
  • <string><map> 在 C++11/C++14 (ISO/GCC) 中
  • <string> 在 C++98 (ISO/GCC) 中

也是一个示例测试 - C++14 (gcc 8.3) - on ideone.com 编译并运行良好:

#include <iostream>

int main() {
    printf("printf without #include <stdio.h>\n");
    return 0;
}

这也适用于 <stdio.h> 中的其他定义,例如 FILE

我在 cppreference.com 上找不到任何信息

我也尝试了几次网络和 SO 搜索,但到目前为止都没有成功。

虽然它对于小例子来说免费获得一些强大的功能很方便,但一个严肃的项目可能会受到影响:除了相对容易修复编译器错误之外,我看到严重运行时的危险错误

我怎样才能有效地控制/防止这种包含?


[1] 引用的代码现在包含 include 语句,但我很确定它没有在我复制它的阶段......或者我可能只是复制了它的一部分? ...无论如何,观察到的行为如上所述。

解决方法

恐怕你不能。

标准要求众所周知的包含文件声明相关名称,但不阻止它们包含其他文件/名称,如果库实现发现它有用。

换种说法,在包含 iostream 之后,您确定属于它的所有名称都已正确声明,但是您无法知道(除非通过检查文件本身)是否定义了其他名称,或者其他标准文件已包含在内。在这里,您的实现选择自动包含 stdio.h,但不同的(标准库)实现可以选择不包含它。你已经到达了不确定性...

,

除了关于“纯度”的任何争论之外,我不认为包含这些函数的声明会以任何方式有害。事实上,拥有它们并让编译器隐式假设 int printf() - 尽管只有 C 编译器这样做,C++ 不会 - 是在自找麻烦。

printf 是 C 语言标准的一部分。尽管 C++ 不是 C,但它们的生态系统之间存在很多重叠,以至于 C++ 也将 printf 视为实现运行时环境的一部分,就像对待 C 标准库中的所有其他函数一样。>

因此,所有这些符号都保留。除了实际实现运行时库本身之外,您无需自己定义这些名称的符号。

#include 预处理器指令只是使用从附加文件中提取的文本进行内联文本替换。

(extern) 符号 声明 不会创建这些名称的符号,但它们会确保您不会重新定义这些符号由你自己。

所以我真的不明白您对运行时错误的担忧从何而来。