编译和链接

//jia.h

int jia(int a,int b);


//jia.c

int jia(int a,int b)
{
        return a+b;
}

//test.c

#include<stdio.h>
#include "jia.h"

int main()
{
      printf("%d\n",jia(1,1));
}

用gcc命令编译test.c(单独用gcc编译一个文件,默认包含了链接的过程,如果只想编译,而不链接,要加-c参数)

gcc test.c

会出现如下错误

/tmp/ccBJQ1Z1.o: In function `main':
test.c:(.text+0x19): undefined reference to `jia'
collect2: error: ld returned 1 exit status

可以发现是错误的引用,连接器ld出错,因为没有找到函数jia的实现文件,需要指出的是#include仅仅添加了一个函数的声明,关于jia的实现,编译器不知道任何信息,因此要手动给出链接信息,正确的做法是,分别编译test.c和jia.c,然后把生成的两个目标文件链接起来。

gcc -c test.c
gcc -c jia.c
gcc test.o jia.o -o test


以下部分转载自http://www.cnblogs.com/webcyz/archive/2012/09/16/2688035.html

要理解头文件,主要是要理解“声明”
C/C++中,所有使用到得变量、函数、类都要是声明过得,就是说,要有一行语句来告诉编译器,我有一个名字叫XXX的???类型的变量(函数、类)。
然后还有一个因素就是,在编译的时候,程序是按照每个.C或.CPP文件单独编译的。
也就是说,对于每个C文件中,如果都用到了同一个函数(比如printf),那么,我在每个对应文件中写一遍printf的声明明显是很麻烦的。所以我把这个声明单独写了一个文件,为了区别,我把扩展名记做.h,在需要使用对应的函数(类)的时候,我就不需要去拷贝函数的声明,而只需要#include对应头文件就可以了,系统自动帮你拷贝进来——C语言提供的头文件,按照函数功能分类好了,比如数学函数就都写在了math.h里面,一包含就全包含,不管你用没用到cos()这个函数或者其他什么。
当然,由于.h文件中也可以包含其他.h文件,所以为了不重复声明或定义,需要用宏做相应的处理,这个不是要理解的东西,而是照着写。


编译的时候,gcc做的比我们想得多得多(包括了链接),想查看gcc都做了什么,添加-v选项,而ld命令仅仅是链接,它不知到main为入口点。

gcc -v -o test test.c



通常编译器并不要求函数一定要在被调用之前定义,编译器在处理到某个未知类型的函数时,会为其创建一个隐式声明,并假设该函数返回值类型为int。但编译器无法检查传递给该函数的实参类型和个数是否正确,所以这不利于编译器为我们排除错误。而且在VC编译器下,这样的代码会编译出错,一般可能提示“ error C2371: 'function1' : redefinition; different basic types”,因为编译器隐式声明了一个function1,VC编译器在后面遇到真实的function1定义时,真实的function1返回值并不是int,而是void。所以VC编译器认为function1函数重定义(redefinition)了。

C99遵循这样的规则:在调用一个函数之前,必须先对其进行声明或定义。调用函数时,如果此前编译器未见到该函数的声明或定义,会导致出错。”
所以,综上所述,尽管在gcc编译器中这样的代码仍然可以编译通过并且正确执行。也最好还是遵循这样的规则:在函数调用前,先对其定义或声明。

通过以上内容分析,我们可以知道,在调用一个函数之前,最好对其进行声明或者定义!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值