4

如果我在运行 ac 程序时不包含头文件会怎样?我知道我收到警告,但程序运行完美。

我知道头文件包含函数声明。因此,当我不包含它们时,编译器是如何计算出来的?它会检查所有头文件吗?

4

5 回答 5

7

I know that I get warnings, but the programs runs perfectly.

That is an unfortunate legacy of pre-ANSI C: the language did not require function prototypes, so the standard C allows it to this day (usually, a warning can be produced to find functions called without a prototype).

When you call a function with no prototype, C compiler makes assumptions about the function being called:

  • Function's return type is assumed to be int
  • All parameters are assumed to be declared (i.e. no ... vararg stuff)
  • All parameters are assumed to be whatever you pass after default promotions, and so on.

If the function being called with no prototype fits these assumptions, your program will run correctly; otherwise, it's undefined behavior.

于 2013-04-26T16:47:29.677 回答
5

在 1989 年 ANSI C 标准之前,没有办法声明函数并指示其参数的类型。你只需要非常小心地使每个调用与被调用的函数保持一致,如果你弄错了编译器不会发出警告(比如传递一个intto sqrt())。在没有可见声明的情况下,您调用的任何函数都被假定为 return int; 这是“隐式 int”规则。许多标准函数确实返回int,因此您通常可以省略#include.

1989 年的 ANSI C 标准(本质上也是 1990 年的 ISO C 标准)引入了原型,但没有强制要求(现在仍然不是)。所以如果你打电话

int c = getchar();

它实际上会起作用,因为getchar()返回一个int.

1999 年的 ISO C 标准删除了隐式规则,并规定在没有可见声明的情况下调用函数int是非法的(实际上是违反约束)。因此,如果您在没有 required 的情况下调用标准函数,则#include符合 C99 的编译器必须发出诊断(可能只是警告)。非原型函数声明(未指定参数类型的声明)仍然是合法的,但它们被认为是过时的。

(2011 ISO C 标准在这个特定领域没有太大变化。)

但是仍然有很多代码是为 C90 编译器编写的,而且大多数现代编译器仍然支持旧标准。

因此,如果您在没有 required 的情况下调用标准函数#include可能会发生的是(a)编译器会警告您缺少声明,并且(b)它将假定函数返回int并采用任何数字和类型您实际传递的参数(也考虑了类型提升,例如shorttointfloatto double)。如果调用是正确的,并且如果你的编译器是宽松的,那么你的代码可能会工作——但如果它失败了,你还要担心一件事,也许是因为一些不相关的原因。

可变参数函数printf是另一回事。即使在 C89/C90 中,printf没有可见原型的调用也有未定义的行为。编译器可以对可变参数函数使用完全不同的调用约定,因此printf("hello")并且puts("hello")可能会生成完全不同的代码。但同样,为了与旧代码兼容,大多数编译器都使用兼容的调用约定,因此例如 K&R1 中的第一个“hello world”程序可能仍会编译和运行。

您还可以为标准函数编写自己的声明;编译器并不关心它是在标准头文件中还是在您自己的源文件中看到声明。但是这样做没有任何意义。声明从标准的一个版本到下一个版本发生了微妙的变化,您的实现附带的标题应该是正确的。

那么如果你调用一个没有相应的标准函数会发生什么#include呢?

在典型的工作环境中,这无关紧要,因为运气好的话,您的程序将无法通过代码审查。

原则上,任何符合 C99 或更高版本的编译器都可能拒绝您的程序并显示致命错误消息。(gcc 会以这种方式表现-std=c99 -pedantic-errors) 实际上,大多数编译器只会打印一个警告。如果函数返回(或者如果您忽略结果)并且所有参数类型都正确,则该调用可能会起作用。如果调用不正确,编译器可能无法打印出良好的诊断信息。如果函数没有返回,编译器可能会假设它返回,你会得到垃圾结果,甚至让你的程序崩溃。intint

因此,您可以研究我的这个答案,通过阅读 C 标准的各种版本来跟进,准确找出您的编译器符合哪个版本的标准,并确定在哪些情况下可以安全地省略#include标头——使用下次修改程序时可能会出现问题。

或者您可以注意编译器的警告(您已使用任何可用的命令行选项启用),阅读您调用的每个函数的文档,#include在每个源文件的顶部添加所需的 s,而不必担心这些东西。

于 2013-04-26T17:23:21.973 回答
1

首先:只包括它们。

如果您不这样做,编译器将为未声明的函数使用默认原型,即:

int functionName(int argument);

因此,如果功能可用,它将编译和链接。但是你会在运行时遇到问题。

于 2013-04-26T16:46:27.110 回答
0

For compatibility with old program C compilers can compile code calling functions which have not been declared, assuming the parameters and return value is of type int. What can happen? See for example this question: Troubling converting string to long long in C I think it's a great illustration of the problems you can run into if you don't include necessary headers and so don't declare functions you use. What happened to the guy was he tried to use atoll without including stdlib.h where atoll is declared:

char s[30] = { "115" };
long long t = atoll(s);
printf("Value is: %lld\n", t);

Surprisingly, this printed 0, not 115, as expected! Why? Because the compiler didn't see the declaration of atoll and assumed it's return value is an int, and so picked only part of the value left on stack by the function, in other words the return value got truncated.

That's why of the reasons it is recommended to compile your code with -Wall (all warnings on).

于 2013-04-26T16:48:09.437 回答
0

如果你省略了标题,有很多事情你不能做:

(我希望从评论中得到更多,因为我的记忆在这方面失败了......)

  • 您不能使用标题中定义的任何宏。这可能很重要。
  • 编译器无法检查您是否正确调用函数,因为标头为它定义了它们的参数。
于 2013-04-26T16:46:11.947 回答