首先,需要注意的是,C标准认为定义与库函数同名的函数是undefined behavior.也就是说,像GCC这样的实现通过将库函数定义为weak symbols来实现这一点,这意味着实现可以定义它们自己的此类函数版本以覆盖库版本.
printf
函数是defined,作为默认链接的标准C库的一部分.在stdio.h文件中是declared.
当您编译第一段代码时,您可能会收到一条警告,说明printf
未声明或正在使用默认声明.函数f
的默认隐式声明是int f()
,即接受未指定数量的参数并返回int
的函数.这(几乎)与实际的声明兼容,因此在实践中它最终可以按预期工作.
第二段代码的问题与缺少标头无关.事实上,由于您的定义与头中的声明兼容,因此这样做不会有错误,并且行为将是相同的.
行为上的差异是由于GCC所做的优化.当它看到对printf
的调用只有一个格式字符串以换行符结尾时,它会将其优化为对puts
的调用,因为可观察到的行为将是相同的.如果您使用-S
进行编译,并查看生成的程序集,您将看到实际发生的情况:
main:
push rbp
mov rbp, rsp
mov edi, OFFSET FLAT:.LC0
call puts // printf call optimized away
mov esi, 2
mov edi, OFFSET FLAT:.LC1
mov eax, 0
call printf // printf call not optimized
mov edi, OFFSET FLAT:.LC2
call puts // printf call optimized away
mov eax, 0
pop rbp
ret
当然,如果给出了用户定义的printf
实现,那么这种优化可能就不适用了.如果使用标志-fno-builtin-printf
进行编译,则不会链接标准的printf
函数,也不会进行这种优化.