C 无头程序
我用C写“hello world”程序C 无头程序,c,linux,compilation,dynamic-linking,C,Linux,Compilation,Dynamic Linking,我用C写“hello world”程序 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
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:~$
这怎么可能?操作系统如何在不包含任何标题的情况下链接库?函数位于C库中(
libc
,在您的例子中),该库是隐式链接的(实际上gcc有一个内置的printf,但它不在点内)
包含头并不会为链接器引入任何函数,它只是通知编译器它们的声明(即“它们看起来像什么”)
显然,您应该始终包含头文件,否则会迫使编译器对函数的外观进行假设
hello.c:2:2:警告:内置函数“printf”的隐式声明不兼容
要处理此警告,应包括头文件(stdio.h
)。您无意中使用了C的一个旧功能,该功能自1999年以来就被弃用了
此外,链接不会失败这一事实意味着默认情况下标准C库是链接进来的。是否包含相关的标题无关紧要。在C中,如果使用标准库函数,则必须包含声明函数的标准标题。对于
printf
您必须包含stdio.h
头文件
在C89(以及GNU C89,默认情况下是gcc
上的语言)中,函数声明有时可以省略,因为有一个称为隐式函数声明的功能:当使用函数标识符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
的有效定义(至少在托管实现上是如此,这可能是您的情况)
如果主函数不接受任何参数,请使用此有效定义:
int main(void) { ... }
编译器使用名为
printf()
的函数引用构建源文件,而不知道它实际使用的参数或其返回类型。生成的程序集包含字符串地址的push
“Hello World”“
在程序的静态数据区域中,然后调用到printf
将对象文件链接到可执行文件时,链接器会看到对printf
的引用,并提供C标准库函数printf()
。巧合的是,您传递的参数(constchar*
)与实printf()
的声明兼容,因此它可以正常工作。但是,请注意,您的程序隐式声明的printf()
具有返回类型int
(我认为),标准printf()
也具有返回类型;但是如果它们不同,并且您要将调用printf()
的结果分配给一个变量,那么您将处于未定义行为的状态,并且可能会得到一个不正确的值
长话短说:#包含正确的头,以获得所用函数的正确声明,因为这种隐式声明不受欢迎,因为它容易出错。头通常只包含函数声明、符号常量和宏定义;它通常不包括函数定义
stdio.h
为您提供的是printf
的原型声明:
int printf(const char * restrict format, ...); // as of C99
printf
的实现位于代码链接所针对的单独库文件中
您的代码“有效”有两个原因:
在C89和早期版本下,如果编译器看到函数调用
在声明或定义该函数之前,它将假定
函数返回int
并接受未指定数量的
参数
printf
的实现返回一个int
,您传入了它
一个恰好与
printf
的实现需要第一个参数
为了回应其他人所说的话,使用intmain(void)
或intmain(intargc,char**argv)
;除非编译器文档明确将void main()
列为合法签名,否则使用它将调用未定义的行为(这意味着从代码运行时没有明显问题到退出时崩溃到无法完全加载)
我说“通常”;我遇到过一些包含代码的标题,但这些标题通常是由不知道自己在做什么的人编写的。在极少数情况下,将代码放入头中是合理的,但通常这是一种不好的做法
我确实认为隐式声明extern int printf()在C89中,对于参数数目可变的函数(如printf
),code>甚至无效gcc
非常适合在c89中编译。