C 语言没有你想象中容易
原文链接 http://yonghaowu.github.io/2018/03/05/C_is_not_easy/
注:以下为加速网络访问所做的原文缓存,经过重新格式化,可能存在格式方面的问题,或偶有遗漏信息,请以原文为准。
/* #include <stdio.h> */
/* #include <malloc.h> */
int main(){
char *c = malloc(10);
c[0] = 'a';
printf("hi, ");
printf("%s\n", c);
free(c);
return 0;
}
为什么这个程序缺了头文件, 依然可以正常编译运行, 并且有正确的结果?
/* #include <stdio.h> */
/* #include <malloc.h> */
/* #include <assert.h> */
int main(){
char *c = malloc(10);
c[0] = 'a';
printf("hi, ");
printf("%s\n", c);
assert(c[0] >= 0.0);
free(c);
return 0;
}
为啥这个程序, 加了 assert 又不行了呢?
- #include 只是把头文件引入进来, 头文件的作用是 包含函数的原型。
- linker(链接器)在链接这一步时,会根据头文件函数的原型去找. o 文件中的函数,然后链接进来
- 对于找不到的函数,各个编译器处理会有不同。gcc、clang 会推断这个函数的原型,如 printf 就是 void printf(char *c, char)
- 推断了原型后,因为每个 C 程序都默认会链接 stdlib 库(gcc 编译里有nostdlib 选项,即不去默认链接 stdlib 的库),所以你正确的使用这个 C 语言函数,也是会找到对应的正确函数
- 所以程序编译时会有警告,依然编译通过,并且能正确运行。
那为什么 assert 就不行了呢? 因为 assert 是一个宏,而不是函数,所以编译器不会像上述那样去处理。当没有引入 assert.h, 编译器便当它是函数来处理,最终 stdlib 里也找不到 assert 这个函数,就报错了。