您的位置:首页 > 编程语言 > C语言/C++

C语言总结之变量的三个属性

2013-11-15 20:18 316 查看
 
      在C语言中,一个变量有三个属性——作用域、链接属性和存储类型。这三个属性决定了一个变量的“可视性”和“生命期”。

        可视性意思是这个变量能在什么地方使用,生命期的意思是它能保持多久。

        修改作用域和链接属性的关键字是extern和static(代码块外的变量声明),修改存储类型的关键字是auto、static(代码块内的变量声明)和register。

        

        标识符(identifier)是一个变量、函数、类型等名字。它们由大小写字母、数字和下划线组成,不能用数字开头,我习惯叫它变量名或简称变量。

1.作用域(scope)

        变量(或标识符)的作用域就是在程序中该变量可以被使用的区域。这意味着两点:

        一、在变量的作用域区域外,此变量(变量名)不再有效;

        二、变量名相同但作用域不同的变量不是同一个变量,所以可以在另外一个作用域起相同的变量名。

        编译器在编译代码的时候可以分辨4中不同类型的作用域——代码块作用域,文件作用域、函数作用域和原型作用域。

1.1 代码块作用域(block scope)

        位于一对花括号之间的所有语句(1)称为一个代码块

        任何在代码块内部的开始位置声明的变量都具有代码块作用域,表示它们可以被这个代码块中的所有语句访问。

        当代码块处于嵌套状态时,里面一层的代码块中的变量在该代码块尾部便终止。但是,如果内层代码块有一个变量名与外层代码块的变量名相同,则内层代码块的变量名会隐藏外层的变量名。

        在K&R C中,函数形参(2)的作用域开始于形参的声明处,位于函数体外(3)。如果在函数体内部声明了名字与形参相同的局部变量,前者将隐藏形参,导致是形参无法访问。

        ANSI C中则终止了这种错误的可能性,它把形参的作用域设定为函数最外层的作用域(整个函数体),这样,声明于函数最外层作用域的局部变量无法和形参同名,因为它们的作用域相同。

注解:

        (1)函数体。函数体是用花括号括起来的若干语句,他们完成了一个函数具体功能。

        (2)形参。全称为“形式参数”,是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传递的参数。

        (3)例如:

int *
find_int(key, array, array_len)
int key;
int array[];
int array_len;
{}




        (4)局部变量。指在程序中,只在特定过程或函数中可以访问的变量,是相对与全局变量而言的。全局变量也称外部变量,是在函数外部定义的,作用域为从变量定义处开始,到本程序文件的末尾。

1.2 文件作用域(file scope)

        任何在所有代码块之外声明的变量都具有文件作用域。它表示这些变量从它们的声明之处开始直到此源文件的结尾处都可以访问。

        在文件中定义的函数名也具有文件作用域。

        在头文件中编写并通过#include 指令包含进来的声明,它们的作用域不局限于头文件的文件尾。

1.3 原型作用域(prototype scope)

        原型作用域只适用于在函数原型中声明的参数名。

        例如:

int a(int b);


        中的int b。

        原型作用域防止这些参数名与函数内部的其他名字冲突。

1.4 函数作用域(function scope)

        函数作用域指只适用于语句标签,语句标签用于goto语句。

        作者希望我永远不要用到这个知识。

2.链接属性(linkage)

        一个程序转换成可执行文件有4个过程——预处理,编译,汇编,连接。链接属性则是在连接这一步作用。变量名的链接属性决定如何处理在不同文件中出现的变量名。变量名的作用域与它的链接属性有关。

2.1 链接属性

        链接属性有三种——外部(external)、内部(internal)和空(none)。

        external链接属性的变量名无论声明多少次,位于几个源文件都表示同一个变量(5)。

        internal链接属性在同一个源文件内所有的声明是同一个变量,不同源文件的多个声明不是同一个变量。

        none链接属性的变量只要被声明一次,就是一个独立的变量,与其它同名变量无关。

        在代码块外面的变量(全局变量)链接属性默认为external,在代码块内部的变量链接属性为none。

        函数名的链接无论在代码块内外默认属性都是none,函数的形参链接属性为none。

2.2 修改链接属性

        关键字extern和static可以修改变量名的链接属性,在声明的时候使用。

        static。如果在声明一个全局变量的时候加上static关键字,可以把这个变量的链接属性改为internal。即这个变量只能在本文件中使用,在其他源文件不能调用。要是在其他源文件也有此变量名,那这个变量是一个全新的变量。

        static只对默认链接属性为external的声明才有改变链接属性的效果。

        extern。一般而言,它为一个变量名指定external链接属性,这样就可以访问在其他任何位置定义的这个变量了。

        当extern用于第一次声明时,对应的变量具有external链接属性,第二次或以后则没有。

注解:

        (5)外部变量。也称为全局变量,它是在函数外部定义的变量。 它不属于哪一个函数,它属于一个源程序文件。其作用域是从定义该变量的位置开始至源文件结束。

3.存储类型(storage class)

        变量的存储类型是指存储变量值的内存类型。变量的存储类型决定变量何时创建、何时销毁以及它的值将保持多久。存储变量的地方有三个——内存、堆栈、寄存器。


3.1 存储类型

        静态变量。凡是在任何代码块之外声明的变量总是存储与静态内存中,也就是不属于堆栈的内存,这类变量称为静态(static)变量。你无法把静态变量的存储地点修改到其他两个地方中。静态变量在程序运行之前创建,在程序的整个执行期间始终存在。

        自动变量。在代码块内部声明的变量默认存储类型是自动的(automatic),存储与堆栈中,成为自动(auto)变量。在程序执行到声明自动变量的代码块时,自动变量才被创建,当程序的执行流离开该代码块时,这些自动变量便自行销毁。

        对于在代码块内部声明的变量,如果加上关键字static,可以是它的存储类型从自动变为静态。

        注意:

        一、修改变量的存储类型并不代表修改该变量的作用域,它的作用域仍然只能在该代码块内部访问。

        二、形参不能声明为静态。

        寄存器变量。寄存器变量存储于机器的寄存器而不是内存。通常,寄存器变量比静态变量和自动变量的访问效率要高。一般来说,编译器会自行决定哪些变量存储到寄存器中。

3.2 初始化

        自动变量和静态变量的初始化有所差别。

        静态变量的初始化。当可执行文件载入到内存时,这个静态变量已经有了初始值。即运行的时候已经有了初始值,如果没有初始值则默认为0。

        自动变量的初始化。自动变量的初始化需要占用资源,因为当程序链接时还无法判断自动变量的存储位置。每次调用函数时,函数的局部变量可能在不同的位置。由于这个原因,自动变量没有默认初始赋值,显示初始赋值编译后相当于在代码块开头插入一条隐藏(隐式)的赋值语句。

        于是产生下面四种后果

        一、无论是在进行声明时初始化还是先声明后初始化,运行效率没有差别;

        二、每次调用函数时,每次都重新初始化;

        三、初始化在运行时执行,你可以用任何表达式作为初始值;

        四、除非对自动变量进行初始化,否则它们的值没用。

参考资料:

Reek K A, 徐波. C 和指针[M]. 人民邮电出版社, 2008.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: