变量的声明和定义

831 篇文章 16 订阅
300 篇文章 0 订阅
搞清楚“声明”( 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语句仅仅是声明变量,编译器虽然知道ab是什么类型的变量,但在链接的时候却找不到它们的地址(因为变量没有被定义,所以编译器没有为它们分配存储空间),于是就出错了。

       不妨看看汇编代码:

$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

可见,编译器根据变量的声明已经知道如何处理外部变量ab,但由于没有定义变量,所以汇编代码中没有由“.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

       然后和上面的代码作一对比,相信很容易区分暂时定义和(正式)定义在汇编代码中的表示。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值