[讨论] 变量的声明和定义

tiankai001   2014-10-9 11:09 楼主
变量的声明和定义
搞清楚“声明”(declaration)和“定义”(definition)之间的区别在理解C语言的过程中非常关键。
       “声明”仅仅是告诉编译器某个标识符是:变量(什么类型)还是函数(参数和返回值是什么)。要是在后面的代码中出现该标识符,编译器就知道如何处理。记住最重要的一点:声明变量不会导致编译器为这个变量分配存储空间。
       C语言专门有一个关键字(keyword)用于声明变量或函数:extern。带有extern的语句出现时,编译器将只是认为你要告诉它某个标识符是什么,除此之外什么也不会做(直接变量初始化除外)。
       先来试一下:
/*Example C code*/
extern int a;
int main(void)
{
       extern int b;
       a = 1;
       b = 2;
       return 0;
}
$gcc test.c
马上你会收到两条出错消息:
undefined reference to a
undefined reference to b
       因为两条extern语句仅仅是声明变量,编译器虽然知道a和b是什么类型的变量,但在链接的时候却找不到它们的地址(因为变量没有被定义,所以编译器没有为它们分配存储空间),于是就出错了。
       不妨看看汇编代码:
$gcc S test.c
$cat test.s
.globl main
       .type       main, @function
main:
       pushl       %ebp
       movl       %esp, %ebp
       subl        $8, %esp
       andl        $-16, %esp
       movl       $0, %eax
       subl        %eax, %esp
       movl       $1, a
       movl       $2, b
       movl       $0, %eax
       leave
       ret
可见,编译器根据变量的声明已经知道如何处理外部变量a和b,但由于没有定义变量,所以汇编代码中没有由“.comm”开头的语句为外部变量分配存储空间,导致最后链接程序找不到有效的符号而报错。
       说完“声明”,现在说“定义”。
       有了对比就很容易明白,定义变量意味着不仅告诉编译器变量的类型,而且编译器同时必须为变量分配空间。定义变量的同时还可以初始化变量,例如:
       int a = 9;
       char c = A;
       在函数里面定义、初始化内部变量已经在前面的文章中讨论过,现在探讨一下外部变量的定义和初始化。
       首先要搞清楚编译器在什么情况下将语句认为是定义,什么情况下认为是声明。这里给出若干原则:
#1 带有初始化的语句是定义
例如:
       int a = 1;        //定义
#2 带有extern的语句是声明(除非对变量进行初始化)
例如:
       extern int a;    //声明
       extern int b = 2;     //定义
#3 既没有初始化又没有extern的语句是“暂时定义”(tentative definition)
例如:
       int a;              //暂时定义
C语言中,外部变量只能被(正式)定义一次:
       int a = 0;
       int a = 0;        //错误!重复定义
又或者:
       int a = 0;
       double a = 0.1;       //错误!标识符a已经被使用
暂时定义有点特殊,因为它是暂时的,我们不妨这样看:
暂时定义可以出现无数次,如果在链接时系统全局空间没有相同名字的变量定义,则暂时定义“自动升级”为(正式的)定义,这时系统会为暂时定义的变量分配存储空间,此后,这些相同的暂时定义(加起来)仍然只算作是一个(正式)定义。
例如:
/*Example C code*/
int a;       //暂时定义
int a;       //暂时定义
int main(void)
{
       a = 1;
       return 0;
}
int a;       //暂时定义
让我们看一下汇编代码:
$gcc S test.c
$cat test.s
.globl main
       .type       main, @function
main:
       ...
       movl       $1, a
       ...
       .comm    a, 4, 4
       程序显示编译器只给变量“a”分配空间一次,尽管C程序中有3个暂时定义语句。
       刚才讲到如果没有相同名字的外部变量定义,则暂时定义会自动变成定义,那么,如果有相同名字的外部变量定义呢?很简单,这时暂时定义的作用相当于声明。
例如:
/*Example C code*/
int a;       //暂时定义
int a;       //暂时定义
int main(void)
{
       a = 1;
      return 0;
}
int a = 0;        //定义
       这里因为定义了外部变量,所以最上面的两个暂时定义就相当于仅仅声明a是int变量。看看汇编代码。
.globl main
       .type       main, @function
main:
       ...
       movl       $1, a
       ...
.globl a                                              //a是一个全局可见的符号
       .data                                          //表示在数据段分配空间(函数的代码会放在代码段)
       .align       4                                 //指示为a分配的地址必须是“4字节对齐”
       .type       a, @object                  //告诉编译器a所代表的空间存放的是数据
       .size        a, 4                              //表示分配给a的空间大小是4个字节
a:                                                      //正式给出符号a的位置
       .long       0                                 //用数值“0”初始化符号a代表的那块存储空间
这里注意,即使不对a进行初始化(.long句),a:这句代码也仍然要出现,因为它告诉编译器分配空间,没有这句话编译器还是不会分配空间。
       回忆一下暂时定义对应的汇编语句:
.comm    a, 4, 4
       然后和上面的代码作一对比,相信很容易区分暂时定义和(正式)定义在汇编代码中的表示。




回复评论 (2)

学习学习~~~~~~~~~~~~~
点赞  2014-10-9 13:40
谢谢楼主的说明 很详细
点赞  2014-10-10 11:05
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复