没有标题的C程序

时间:2012-01-19 17:37:04

标签: c linux compilation dynamic-linking

我写了#34;你好世界" C节目。

void main()
{ printf("Hello World"); }
// note that I haven't included any header file

该程序编译时带有警告

vikram@vikram-Studio-XPS-1645:~$ gcc hello.c 
hello.c: In function ‘main’:
hello.c:2:2: warning: incompatible implicit declaration of built-in function ‘printf’
vikram@vikram-Studio-XPS-1645:~$ ./a.out 
Hello Worldvikram@vikram-Studio-XPS-1645:~$

这怎么可能?操作系统如何在不包含任何标题的情况下链接库?

5 个答案:

答案 0 :(得分:7)

printf函数位于C库中(在您的情况下为libc),它是隐式链接的(实际上gcc有一个内置的printf,但它在点之外)。

包含头文件不会为链接器带来任何函数,它只是通知编译器它们的声明(即“它们看起来像”)。

显然你应该总是包含头文件,否则你会强迫编译器假设函数是什么样的。

答案 1 :(得分:6)

编译器使用对名为printf()的函数的引用构建源文件,而知道它实际需要什么参数或返回类型是什么。生成的程序集在程序的静态数据区域中包含push字符串"Hello World"的地址,后跟callprintf

将目标文件链接到可执行文件时,链接器会看到对printf的引用,并提供C标准库函数printf()。通过巧合,您传递的参数(const char*)与真实printf()的声明兼容,因此它可以正常运行。但请注意,您的程序隐式声明的printf()返回类型int(我认为),标准printf()也有;但如果它们不同,并且您要将调用printf()的结果分配给变量,那么您将处于未定义行为的范围内,并且您可能会得到不正确的值。

长话短说:#include正确的标题可以为您使用的函数获取正确的声明,因为这种隐式声明已被弃用,因为它容易出错。

答案 2 :(得分:1)

在C中,如果使用标准库函数,则必须包含声明函数的标准头。对于printf,您必须包含stdio.h标头文件。

在C89(以及gcc上默认为语言的GNU C89)中,有时可以省略函数声明,因为有一个称为隐式函数声明的特性:当函数标识符foo是如果已使用且函数尚未声明,则实现将使用此声明:

 /* foo is a function with an unspecified number of arguments */
extern int foo();

但是这个声明仅适用于返回带有未指定但固定个参数的int的函数。如果函数接受可变数量的参数(如printf),则此程序将调用未定义的行为。

这是C89 / C90所说的:

  

(C90,6.7.1)“如果定义了接受可变数量参数的函数而没有以省略号表示法结尾的参数类型列表,则行为未定义。

所以即使在C89和GNU C89中,gcc也足够编译:编译器可以拒绝编译。

另请注意

void main() { ... }

不是main的有效定义(至少在托管的实现上可能是你的情况)。

如果你的main函数没有接受任何参数,请使用这个有效的定义:

int main(void) { ... }

答案 3 :(得分:1)

标题通常 1 仅包含函数声明,符号常量和宏定义;它通常不包括函数定义。

所有stdio.h都会为您提供printf的原型声明:

int printf(const char * restrict format, ...); // as of C99

printf实现位于您的代码链接的单独库文件中。

您的代码“有效”有两个原因:

  1. 在C89及更早版本中,如果编译器看到函数调用 在该函数的声明或定义之前,它将假定 该函数返回int并取一个未指定的数字 参数;

  2. printf的实现会返回int,您就会传入 一个恰好恰好兼容的论点 printf的实现需要第一个参数。

  3. 要回应其他人的说法,请使用int main(void)int main(int argc, char **argv);除非您的编译器文档显式void main()列为合法签名,否则使用它将调用未定义的行为(这意味着从运行时没有明显问题的代码到退出时崩溃到无法完全加载)。


    1. 我说“通常”;我遇到了一些包含代码的标题,但这些标题通常是由那些不知道自己在做什么的人编写的。可能在极少数情况下将代码置于标题中是合理的,但通常这是不好的做法。

答案 4 :(得分:0)

  

hello.c:2:2:警告:内置函数'printf'不兼容的隐式声明

要处理此警告,您应该包含头文件(stdio.h)。您无意中使用了自1999年以来已弃用的旧C功能。

此外,链接不会失败的事实只是意味着默认情况下链接标准C库。您是否包含相关标题并不重要。