您的位置:首页 > 编程语言 > C语言/C++

[C语言]为什么要有include?——从Hello World说起

2016-05-13 15:23 483 查看
本文转自:http://mp.weixin.qq.com/s?__biz=MzAwOTgzNzQyMw==&mid=433613487&idx=1&sn=803995d612faadce6e4418789a6a65a8&scene=2&srcid=0312ElIT9UmR0ZygPGHxDxs2&from=timeline&isappinstalled=0#wechat_redirect

先来看看造物者说的第一句话(漫画来源于网络,如有侵权请告知)



对于C语言来说,造物者当然是Dennis Ritch啦~就是本文封面的那位。

大家都会写的Hello World程序长这个模样:

#include <stdio.h>

int main( ){

printf("Hello world!\n");

return 0;

}

看,关键的就是第三行,调用了printf函数,在屏幕上打印了伟大的"Hello world!"。第一行那个include是个啥?我听到过这样的解释:“include是包含的意思,尖括号里的stdio.h是标准输入输出函数库文件,这个文件里有printf的代码,把这个代码包含到main函数之前,就可以使用printf函数进行打印啦~” —— 打住!这种话骗骗刚开始学习C语言的小孩还可以,怎么可以用来骗大学生呢?现在请大家把第一行删除,重新编译运行看看?奇葩,居然依然可以顺利执行!但是细心的孩纸会发现,这次尽管成功运行了,但是编译时有一条警告(Warning)信息,如果你的编译器是gcc的话,这条Warning的样子应该是:

warning: implicitly declaring library function 'printf' with type 'int (const char *, ...)'

What?这是咩意思?冷静一下,请大家不要一看到英文就头皮发麻、六神无主、举手问老师。至少可以翻翻词典嘛,前两个词就很明白了:隐含声明。先不解释,我们继续修改代码,变成这样:

int main(){

printf("Hello world!\n");

hehe(3, 5);

return 0;

}

然后大家就笑了:这肯定不能运行了吧?那个hehe()函数根本就不存在嘛!没错,这个确实不能运行了,但是依然可以编译。据我所知大家都很偷懒,编好程序之后都是直接在集成开发环境(IDE)中直接点运行按钮的,那么这次请点击编译按钮,或者在命令行中手动输入编译命令,我们就能发现,又多了一条Warning:

warning: implicit declaration of function 'haha' is invalid in C99

又是“隐含声明”!

为什么printf和hehe两个函数都提示“隐含声明”,前一个能运行,而后一个不行呢?我们从头梳理:

首先,了解一下什么叫做函数的“声明”(或者叫“原型”)。函数声明就是描述一下这个函数长什么样子,而不描述这个函数具体怎么实现的。举个例子:
void hehe(int, int); 就是一个函数的声明,注意,括号后面是一个分号,而不是一对花括号{ },而且括号里面只写了参量类型,没有写参量名啊。函数的声明是给编译器看的,当编译器看到上面那个声明,她就会知道:哦,有一个函数,名字叫hehe,带有两个整型参量,没有返回值。至于这个函数具体是干什么的,编译器不关心~
同样,编译器也不关心这个函数的两个参量叫什么名字,所以列表中可以只写类型,不写参量名。

然后,我们大家都学过:C语言规范要求在调用函数之前,必须出现该函数的声明。这是为什么呢?是为了让编译器高兴啊。编译器从上往下检查代码,当看到函数调用时,就会对照此前出现过的声明检查一下调用是否符合声明的格式。如果冷不丁地看到一个函数调用,但在此之前没见到过该函数的声明,编译器就郁闷了,她的内心活动是:“这函数,没见过、不认识啊,所以,这么个调用法,到底是正确呢?还是错误呢?我不知道啊!我作为一个编译器,不知道这句是正确还是错误,多没面子啊!”这时候,编译器能报一个“错误”(Error)吗?不能。但是编译器究竟还是负责人的,她觉得这句话可能会有问题,因此给了一个警告,让程序员自己看着办……

接下来,分析一下为什么printf没有声明,能够正确执行。大家可以翻翻前面我说的那个“骗小孩子”的话,stdio.h里面并没有printf的实现代码,而是有printf的声明(请在自己计算机中自行搜索stdio.h,并打开文件查找printf,看看是如何声明的)。printf的实现代码,是在C标准库的其它文件中(随着OS不同,具体文件也不同)的,而且是编译过之后的代码,不像stdio.h中,都是“源代码”。如果把include那行删去,编译器在看到printf调用时,就不知道printf应该长成啥样,所以不知道调用是否正确,给了一个警告。但是在编译之后的“链接”过程,C标准库就被链接到程序中了,所以只要printf的调用写得没有错误,就可以正确运行。

最后,分析为什么添加hehe(3,5)之后能编译,但是不能正确执行。道理与上面是一样的,编译器看到hehe(3,5)的调用,因为没有声明过,她不认识这函数啊,所以,不能报错,只能给一个警告。接下来的步骤,链接,C标准库依然被链接到了程序中,但是……但是……但是……hehe( )是个什么鬼?这函数根本就不在C标准库中好吗!而且程序员自己所写的程序中也没有提供hehe(
)的实现代码啊!所以链接失败……

哦,似乎忘了说,C代码写好之后要想运行,得经过这么几步:(1)对每个源文件分别进行编译(compile),各自生成目标代码(扩展名可能是.obj .o 或者其它)(2)把编译过后生成的这些目标代码以及C标准库链接起来(link),生成可运行的程序。

最后放一条冷笑话:某个峡谷上方架有一座桥,一次暴风雨过后,桥断了,负责维护的工人一时间还来不及修复,于是在桥头插了一块牌子,上面写道:“Warning! The bridge is broken!” 结果呢?还是有好多人毅然走上断桥,摔到了峡谷里。工人们感到很奇怪,于是到了谷底查看,发现摔到谷底的全都是程序员。

上面的笑话没看懂?没关系,作为程序员,记住下面这一点就行了:绝不放过任何一个warning,否则可能会死得很惨~~~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: