恕我直言,请始终使用-Werror=implicit-function-declaration
编译选项来防止编译器/链接器的智能默认行为造成损坏。
通过将这两个案例编译成两个可执行文件sleep_include_no
(第一个错误案例,不包含)和sleep_include_yes
(第二个案例,包含包含),我做了一些简单的测试:
## 'sleep' will invoke the 'nanosleep', and use strace to show real duration
$ strace ./sleep_include_no 2>&1 | grep nanosleep
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=1, tv_nsec=0}, 0x7ffee0165970) = 0
$ strace ./sleep_include_yes 2>&1 | grep nanosleep
clock_nanosleep(CLOCK_REALTIME, 0, {tv_sec=0, tv_nsec=0}, 0x7ffce92eedd0) = 0
disas
来自 gdb命令输出的 asm 代码片段:
/* 1st wrong case (without include), compiler/linker's default behavior, \
an immediate constant '0x1' is set into %eax as parameter of sleep */
=> 0x0000555555555141 <+8>: movsd 0xebf(%rip),%xmm0 # 0x555555556008
0x0000555555555149 <+16>: movsd %xmm0,-0x8(%rbp)
0x000055555555514e <+21>: mov -0x8(%rbp),%rax
0x0000555555555152 <+25>: movq %rax,%xmm0
0x0000555555555157 <+30>: mov $0x1,%eax
0x000055555555515c <+35>: call 0x555555555030 <sleep@plt>
/* 2nd ok case (with include), \
the 'cvttsd2si' instruction is something like cast double to int */
=> 0x0000555555555141 <+8>: movsd 0xebf(%rip),%xmm0 # 0x555555556008
0x0000555555555149 <+16>: movsd %xmm0,-0x8(%rbp)
0x000055555555514e <+21>: movsd -0x8(%rbp),%xmm0
0x0000555555555153 <+26>: cvttsd2si %xmm0,%rax
0x0000555555555158 <+31>: mov %eax,%edi
0x000055555555515a <+33>: call 0x555555555030 <sleep@plt>
所以编译器/链接器只是让它工作,但可能不是你所期望的。我认为这是因为编译器有很多遗留特性需要处理和兼容性考虑,我们不应该责怪它。作为一名程序员,我唯一能做的就是使用-Werror=implicit-function-declaration
发出强制警报。
PS 分享一个由于不包含用户定义的 API 头文件和忽略函数警告的隐式声明而引发的血腥生产错误。这是演示代码(3个源文件):
$ cat my_lib.h
#ifndef _my_lib_h_
#define _my_lib_h_
long long get_i64_from_my_lib();
#endif
$ cat my_lib.c
#include "my_lib.h"
long long get_i64_from_my_lib() {
return 113840990519587; /* 6789 ABCD 0123 */
}
$ cat main.c
#include <stdio.h>
/* #include "my_lib.h" (without this include, the result is buggy) */
int main() {
long long i64 = get_i64_from_my_lib();
printf("%lld, %0lx\n", i64, i64);
}
$ gcc -g -c my_lib.c
$ ar -cq my_lib.a my_lib.o
$ gcc -g -o my_exe main.c my_lib.a ## emit implicit-function-declaration warning
## The returned type is not an expected i64, but a truncated i32. \
## When the returned value is less than 2^31, the function seems ok, \
## so it is an evil bug.
$ ./my_exe
-1412628189, ffffffffabcd0123