from:http://www.cnblogs.com/safeking/archive/2007/03/13/672773.html
在很多编码规则都有这样一条,变量与函数的声明放在头文件中,它们相应的定义放在源文件中。
可是很多人尤其是初学者对声明和定义分辨不清,觉得声明即是定义,定义即是声明。造成这种混淆
并不奇怪,因为变量在大多数情况下,声明的同时就定义了。
e.g.
//main1.c
int var; //声明的同时也对其定义了
int main(int argc, char **argv[])
{
...
}
而如下是纯粹的声明,没有包含定义
e.g.
//main2.c
extern int val; //仅仅声明这是个外部变量,extern的具体作用在后面讲。
extern int val1 = 1; // 声明并定义外部变量
int main(int argc, char **argv[])
{
...
}
而函数的声明与定义也是类似的。而且要更容易区分。
e.g.
//main3.c
void func() //即是声明也是定义
{
...
}
int main(int argc, char **argv[])
{
...
func(); //可以编译链接
...
}
仅仅对函数声明的话,就不需要写函数体,只要声明函数类型以及函数形参的信息就可以了。
e.g.
//main4.c
void func(char); //仅仅是声明
int main(int argc, char **argv[])
{
...
func(); //编译可以顺利通过,如果后面没有函数定义的话链接将会失败
...
}
void func(char *str) //函数定义,如果此文件中没有这部分定义的话,main中可以编译,但不能链接
{
...
}
之所以函数的定义与声明比较好区分是因为函数必须得有函数体编译器才能给它分配空间,而变量仅仅需要要个名字和它的类型编译器就
可以分配空间给它。
所以可以这样说,声明只是告诉编译器声明的变量和函数是存在的,但并没有真正分配空间给它,所以当后面的代码
用到前面声明的变量或函数时,编译器在编译的时候不会报错,但是在链接的时候会报错,因为链接的时候编译器会去
寻找这些变量和函数的内存地址,因为只声明了但没定义,链接器当然找不到它们了,所以就报错了。而对它们进行定义
了的话编译器就会给它们分配空间,它们就有自己的地址了,这时就能链接了。
所以定义是要分配空间的,所以定义只能有一次。而声明不分配空间,你可以声明多次。
extern关键字
我今天写这边短文就是因为有人问我extern的用法,他们知道怎么样extern,但是对extern原理不清楚,知其然不知其所以然。
上面main2.c中的extern int val; 它的作用就是告诉编译器这个变量是在其他文件中定义的(是外援),要是在本文件中
看到它的名字千万别奇怪。编译器是相信自己人的,所以在编译的时候要是看到val变量时会认为它是存在,不会报错。只有在
链接的时候链接器才会去其它obj文件中寻找val变量的定义(地址),找到则顺利链接,否则报错。因为编译器只需要知道extern所
声明变量的名字就可以了,所以extern int val 可以写成 extern val(即省略变量类型)。
e.g.
//main2.c
extern int val; //声明val是个外部变量,也可以写成extern val;
int main(int argc, char **argv[])
{
val = 10;
...
}
//another.c
int val;
把这两个文件一起编译链接是没有问题的。
并不是所有的变量都能用extern声明,只有全局变量并且没有被static 声明的变量才能声明为extern.
所以如果你不想自己源文件中全局的变量被其他文件引用,你就可以给变量加上static声明
e.g.
extern val; //链接的时候会报错,因为val被声明为static.
int main(int argc, char **argv[])
{
val = 10;
...
}
//another.c
static int val;
这也是static全局变量和非static全局变量的唯一的一个区别。