C语言中声明和定义的区别——分析extern关键词。

时间:2021-09-03 06:54:21

一直很迷惑C语言中的声明和定义的有些实践中的用法,说迷惑实践是因为声明和定义的概念上的区别是很明确的。

定义和声明的区别(主要针对变量):

定义是要为变量分配存储空间,还可以在定义的时候为变量指定初始值。在一个程序中,变量有且仅有一次定义。

声明用于向程序表明变量的类型和名字。定义包括声明:定义变量时我们声明了变量的类型和名字。可以使用extern关键字声明变量名而定义它。不定义变量的声明包括变量名,变量类型和变量类型前的关键字extern。


声明的迷惑,我们可不可以对一个局部变量进行多次声明,既然只要有了定义,我们就可以进行多次声明,那么局部变量定义之后是否可以多次声明,个人认为是不能对一个局部变量进行声明(除了定义时包含的声明外,是不能再做声明的), 因为再次声明的形式应该是extern 类型 变量名,但是extern本身的作用是针对全部变量声明的,所以不可以对局部变量使用extern。

网上有些人说在全局变量 int a 和extern int a是一样的,因为编译器会默认给int a添加extern的,实质上这里也很迷惑的。首先extern本身的意思是外部的,全局的意思,与internal对应,internal主要指的是局部变量,在函数外部定义的变量本身就是全局的,默认是extern的,可以被其他文件访问,但是与extern int a写法中所含的意思是否一样还有待深究。

依据百科百度来说int a和extern int a 区别还是很大的,extern 置于函数或变量前,表示变量或函数的定义在别的文件(也可能本文件中), 在链接的时候才会解析符号表,暂时不清楚,如果在同一个文件中先做extern声明,再定义,符号解析会不会特殊处理,当然如果不想了解太底层的话,可以明确在一个文件中先extern声明再定义,与不同文件中extern声明和定义的表现是一样的。

下面对变量先声明再定义的形式很像函数先定义后声明(唯一不同点是声明函数时我们经常省略掉extern关键字,是因为函数声明默认就是extern的),下面的代码在gcc下正常输出b的值。

<pre name="code" class="cpp">#include<stdio.h>
extern int b;
int main(void)
{
printf("%d\n", b);
return 0;

}
int b = 4;


 

我们来分析这三条语句有什么区别:

//都为全局变量

int a;

extern int a;

extern int a = 0;

int a直接定义全局变量,很明确的定义, extern int a仅仅只是声明,没有定义的情况下声明是会报错的。extern int a = 0定义a, 与int a = 0效果一样,但是在gcc下使用extern int a = 0和int  a = 0还是会有一个小小的却别,int a= 0是不会报错或警告,但是使用extern int a = 0会报警告, 但是表现效果是一样的。

<pre name="code" class="cpp">#include <stdio.h>
extern int a = 5;

int main(void)
{
printf("%d\n", a);
return 0;
}
//<pre name="code" class="cpp"><span style="background-color: rgb(255, 255, 255);">警告信息: </span><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">warning: 'a' initialized and declared 'extern' [enabled by default] </span>
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">extern int a = 5;</span>
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span>

 
 
<pre name="code" class="cpp">//但是下面extern int a = 0和int a = 0;出现了错误信息:
 
<pre name="code" class="cpp"><pre name="code" class="cpp">#include <stdio.h>extern int a = 5;int main(void){      printf("%d\n", a);     return 0;}int a = 0;

 


 
warning:  
'a' initialized and declared 'extern' [enabled by default]  

extern int a = 5;

error: redefinition of 'a'

int a = 0

note: previous definition of 'b' was here

extern int a= 5;


在给读者看这样一段代码:

#include <stdio.h>
extern int a = 5;

int main(void)
{
printf("%d\n", a);
return 0;
}
int a;

以我们上面的分析, 编译时应该出现变量a redifition(重定义)的错误,但编译结果出来时,可以有些初学者会很迷惑居然没有报错,只是对extern int a = 5进行了警告。为什么会与我们上面分析的结果不同呢,其实这里涉及到了C语言的另一种机种,强符号与弱符号,强符号和弱符号都是针对全局变量而定,何谓强符号,何谓弱符号,强符号是定义的时候初始化,弱符号定义时未初始化,在C语言中对于相同变量进行定义,要求只能存在一个强符号,允许弱符号的存在,所有上面代码中两句全局变量的声明int a = 0;

int a;也是不会报错的,关于强符号和弱符号的详细知识可以参详《程序员自我修养》关于强符号和弱符号章节。


通过上面的分析,我们总结如下:

1、局部变量基本就是定义时包含声明,应该没有定义后还有声明的情况(至少我自己想不到特殊的情况,因为声明是需要使用extern的,而extern是针对全局变量的)

1、extern是针对全局变量和全局函数的。

2、非static全局函数默认是extern的(为什么要把static有extern区分开, 有说法是static和extern是水后不相容的,也就是说extern是可以被外部文件访问,但是static相反会将访问限制在本文件中)

3、在我们读代码的时候全局变量int a = 0 和 extern int a = 0表现效果是一样的,但是对仅仅是extern int a这样的声明,一定要在其他文件或本文件的其他地方存在定义,也就是做最后符号链接时一定要有其他地方进行定义。

读者可以对比函数和全局变量的extern性质,他们是很相似的,它们之间一个大的区别应该是exter int tt和int tt, 其中tt既可以表示函数名也可表示变量名,对于函数来说都是声明,因为默认加extern,但是对于变量来说无extern时定义,加extern是声明。



看一下,我随机想出来的几个变量声明和定义,是否合理正确:

int a;
int main()
{
int a;
}


分析:正确,但是涉及的作用域覆盖问题,main函数内的变量a和全局变量a是不同的变量。

----------------------------------

int a;
int main()
{
exteran int a;
}


分析:正确,extern int a声明的变量a就是全局变量定义的a,其实在main里面不作声明也是可以直接使用全局变量a的,这里面也是因为作用域的问题。

----------------------------------

extern int a;
int main()
{
int a;
}

分析:是否通过主要看a是否在其他文件中有定义,因为extern int a只是做了声明,而main函数里的变量a其实与extern int a声明的变量a是没有关系的。

-----------------------------------

extern int a
int main()
{
extern int a;
}


分析:是否通过主要看a是否在其他文件中有定义,extern int a声明的变量a就是全局变量声明的a,其实在main里面不作声明也是可以直接使用全局变量a的,这里面也是因为作用域的问题。

分析下面语句的正确性(主要有自己的分析过程,下面都是正确的):

extern int pi = 4;当这条声明位于函数外部时,并且含有初始化式时,才算定义了pi, 并分配了存储空间。但是在函数体内则报错。extern是外部的意思,外部变量只能定义在函数外部。

extern应用场合:任何在多个文件中使用的变量都需要与定义分离的声明。在这种情况下,一个文件含有变量的定义,使用该变量的其他文件则包含该变量的声明(而不是定义), 因为变量须且仅能定义一次,而变量的声明可以多次。