标签:
在上一讲中,简单介绍了函数的定义和使用,只要你想完成一个新功能,首先想到的应该是定义一个新的函数来完成这个功能。这讲继续介绍函数的其他用法和注意事项。
1 int sum(int a, int b) { 2 return a + b; 3 } 4 5 int main() 6 { 7 int c = sum(1, 4); 8 return 0; 9 }
第5行定义的main函数调用了第1行的sum函数,这是合法的。如果调换sum函数和main函数的顺序,在标准的C编译器环境下是不合法的(不过在GCC编译器环境下只是一个警告)
1 // 只是做个函数声明,并不用实现 2 int sum(int a, int b); 3 4 int main() 5 { 6 int c = sum(1, 4); 7 return 0; 8 } 9 10 // 函数的定义(实现) 11 int sum(int a, int b) { 12 return a + b; 13 }
在第11行定义了sum函数,在第2行对sum函数进行了声明,然后在第6行(main函数中)就可以正常调用sum函数了。
1> 格式
返回值类型 函数名 (参数1, 参数2, ...)
只要你在main函数前面声明过一个函数,main函数就知道这个函数的存在,就可以调用这个函数。而且只要知道函数名、函数的返回值、函数接收多少个参数、每个参数是什么类型的,就能够调用这个函数了,因此,声明函数的时候可以省略参数名称。比如上面的sum函数声明可以写成这样:
int sum(int, int);
究竟这个函数是做什么用的,还要看函数的定义。
2> 如果只有函数的声明,而没有函数的定义,那么程序将会在链接时出错
下面的写法是错误的:
1 int sum(int a, int b); 2 3 int main() 4 { 5 6 sum(10, 11); 7 8 return 0; 9 }
1> 在编写第一个C程序的时候已经提到:我们编写的所有C语言代码都保存在拓展名为.c的源文件中,编写完毕后就进行编译、链接,最后运行程序。
2> 在前面的学习过程中,由于代码比较少,因此所有的代码都保存在一个.c源文件中。但是,在实际开发过程中,项目做大了,源代码肯定非常多,很容易就上万行代码了,甚至上十万、百万都有可能。这个时候如果把所有的代码都写到一个.c源文件中,那么这个文件将会非常庞大,也非常恶心,你可以想象一下,一个文件有十几万行文字,不要说调试程序了,连阅读代码都非常困难。
3> 而且,公司里面都是以团队开发为主,如果多个开发人员同时修改一个源文件,那就会带来很多麻烦的问题,比如张三修改的代码很有可能会抹掉李四之前添加的代码。
4> 因此,为了模块化开发,一般会将不同的功能写到不同的.c源文件中,这样的话,每个开发人员都负责修改不同的源文件,达到分工合作的目的,能够大大提高开发效率。也就是说,一个正常的C语言项目是由多个.c源文件构成。
接下来就演示一下多个源文件的开发,我将前面定义的sum函数写在另一个源文件(命名为sum.c)中。这时候就有两个源文件:
1 int main() 2 { 3 4 return 0; 5 }
1 int sum(int a, int b) 2 { 3 return a + b; 4 }
1> 现在想在main函数中调用sum函数,那么你可能会直接这样写:
1 int main() 2 { 3 int c = sum(10, 11); 4 5 return 0; 6 }
这种写法在标准C语言编译器中是直接报错的,因为main函数都不知道sum函数的存在,怎么可以调用它呢!!!
2> 我们应该骗一下main函数,sum函数是存在的,告诉它sum函数的返回值和参数类型即可。也就是说,应该在main函数前面,对sum函数进行声明。
main.c文件应该写成下面这样
1 #include <stdio.h> 2 3 int sum(int, int); 4 5 int main() 6 { 7 int c = sum(10, 11); 8 9 printf("c is %d\n", c); 10 11 return 0; 12 }
注意第3行,加了一个sum函数的声明。为了检验sum函数的调用结果,在第9行用prinf函数将结果输出。
sum.c和main.c都编写完毕后,就可以使用gcc指令进行编译了。同时编译两个文件的指令是:cc -c main.c sum.c
编译成功后,生成了2个.o目标文件
也可以单独编译:
cc -c main.c
cc -c sum.c
前面已经编译成功,生成了main.o和sum.o文件。现在应该把这2个.o文件进行链接,生成可执行文件。
1> 注意,一定要同时链接两个文件。如果你只是单独链接main.o或者sum.o都是不可能链接成功的。原因如下:
可以看出,main.o和sum.o有密不可分的关系,其实链接的目的就是将所有相关联的目标文件和C语言函数库组合在一起,生成可执行文件。
2> 链接main.o和sum.o文件:cc main.o sum.o,生成了可执行文件a.out
3> 运行a.out文件:./a.out,运行结果是在屏幕上输出了:
c is 21
说明函数调用成功,我们已经成功在main.c文件的main函数中调用了sum.c文件中的sum函数
4> 从中也可以得出一个结论:只要知道某个函数的声明,就可以调用这个函数,编译就能成功。不过想要这个程序能够运行成功,必须保证在链接的时候能找到函数的定义。
理解完前面的知识后,接下来就可以搞懂一个很久以前的问题:每次写在最前面的#include是干啥用的?
先来看一个最简单的C程序:
1 #include <stdio.h> 2 3 int main() 4 { 5 printf("Hello, World!\n"); 6 return 0; 7 }
这个程序的作用是在屏幕上输出Hello,World!这一串内容,我们主要关注第一行代码。
也就是说你完全可以将第3行~第7行的代码放到其他文件中,然后用 #include 指令包含进来,比如:
1> 将第3行~第7行的代码放到my.txt中
2> 在main.c源文件中包含my.txt文件
上面的#include "my.txt"使用的是相对路径,其实也可以使用绝对路径。比如#include "/Users/apple/Desktop/my.txt"
二者的区别在于:当被include的文件路径不是绝对路径的时候,有不同的搜索顺序。
1> 对于使用双引号""来include文件,搜索的时候按以下顺序:
2> 对于使用尖括号<>来include文件,搜索的时候按以下顺序:
我这里使用的是clang编译器,clang设置include路径是(4.2是编译器版本):/usr/lib/clang/4.2/include
Mac系统的include路径有:
我们已经知道#include指令的作用了,可是为什么要在第一行代码包含stdio.h呢?
跟printf函数一样,我们在开发中会经常将函数的声明和定义写在不同的文件中,函数声明放在.h头文件中,函数定义放在.c源文件中。
下面我们将sum函数的声明和定义分别放在sum.h和sum.c中
这是sum.h文件
这是sum.c文件
然后在main.c中包含sum.h即可使用sum函数
其实sum.h和sum.c的文件名不一样要相同,可以随便写,只要文件名是合法的。但还是建议写成一样,因为一看文件名就知道sum.h和sum.c是有联系的。
运行步骤分析:
1> 在编译之前,预编译器会将sum.h文件中的内容拷贝到main.c中
2> 接着编译main.c和sum.c两个源文件,生成目标文件main.o和sum.o,这2个文件是不能被单独执行的,原因很简单:
* sum.o中不存在main函数,肯定不可以被执行
* main.o中虽然有main函数,但是它在main函数中调用了一个sum函数,而sum函数的定义却存在于sum.o中,因此main.o依赖于sum.o
3> 把main.o、sum.o链接在一起,生成可执行文件
4> 运行程序
大家都知道#include的功能是拷贝内容,因此上面的代码等效于:
这么一看,语法上是绝对没有问题的,main.c、sum.c都能编译成功,分别生成sum.o、main.o文件。但是当我们同时链接main.o和sum.o时会出错。原因:当链接这两个文件时链接器会发现sum.o和main.o里面都有sum函数的定义,于是报"标识符重复"的错误,也就是说sum函数被重复定义了。默认情况下,C语言不允许两个函数的名字相同。因此,不要尝试去#include那些.c源文件。
【零基础学习iOS开发】【02-C语言】11-函数的声明和定义
标签:
原文地址:http://www.cnblogs.com/LiLihongqiang/p/5686720.html