《C和指针》读书笔记
变量具有三大属性:作用域、链接属性、存储类型
这三个属性决定了一个变量可以在什么地方使用(“可视性”)和它的值会保留多久(“生命周期”)
作用域
标识符能够被使用的区域就是标识符的作用域。
就比如:函数的局部变量的作用域在这个函数的函数体内。也就意味着其他的函数不能够通过这个变量的名字访问到它们。
编译器对变量划分出了4种类型的作用域---->文件作用域、代码块作用域、函数作用域和原型作用域。当分属不同的作用域时,不同的变量可以取相同的名字,但是不建议这样。
文件作用域
在所有代码块外面声明的变量都具有文件作用域
表示从开始建立–>这个变量所在源文件的结尾处 都可以访问。文件中定义的函数名也具有文件作用域
代码块作用域
在一对合法的花括号之内的所有语句就是一个代码块
在代码块的开始位置声明的标识符具有代码块作用域,该标识符在在代码块中都可以被访问。比如6、7、9、10处声明的变量在它所在的代码块间都可以被访问,此外,定义函数时的形参也具有代码块作用域。
当代码块的外面和代码块里面都有一个名字相同的标识符时,如果在代码块内使用标识符,到底用代码块之外声明的标识符还是代码块里面的呢?其实外面的那个标识符会被隐藏。也就意味着在代码块的里面不能通过名字来访问到它。比如6和9处声明的 f ,代码块里使用的是9处声明的。
而函数体内如果声明了名字和形参相同的局部变量时,就会隐藏形参。
不是嵌套的状态时,编译器可以将两个代码块的变量存储在同一个内存地址,因为两个非嵌套的代码块最多只有一个处于活动状态
原型作用域
适用于函数原型中的声明参数名。
函数作用域
适用于语句标签,语句标签适用于goto语句。一个函数中的语句标签必须是唯一的
链接属性
标识符的链接属性决定了如何处理不同文件中出现的标识符。
标识符的链接属性有三种:
external(外部链接属性)
internal(内部链接属性)
none(无链接属性)
😃 external链接属性的标识符不管声明多少次、位于几个源文件表示的是同一个实体
😃 internal链接属性的标识符在一个源文件中的声明都指向同一个实体,不同源文件的声明则是不同的实体
😃 none链接属性的标识符的多个声明都是独立不同的实体。
函数名的链接属性属于external,当一个源文件调用函数时,会链接到其他文件所定义的函数,这个函数的定义也有可能是库函数里的。
链接属性这个特点离不开关键字extern和static
static可以使具有external链接属性的声明修改为internal的链接属性
,也就是只能在那个所声明的源文件中使用。
static int A;
static int B (int c)
变量A只能在它声明所在源文件使用
函数B只能在它声明所在的源文件调用
external只于标识符的第一次声明才有用,第二次及以上的声明将不会改变链接属性,它可以使一个标识符访问在其他任何位置定义的这个实体
存储类型
变量的存储类型是什么?
指的是存储变量值的内存类型。
变量可以存储在普通内存、堆栈、寄存器中。存储的地方不同,特性也不同。按照存储类型可以将变量分成:静态变量
和自动变量
在代码块外面的变量不属于堆栈的内存,总是存储在静态内存中,称为静态变量。
在代码块内部的变量属于堆栈的内存,其存储类型是自动的,称为自动变量。一般用auto修饰,但是变量一般情况下是自动变量,所以很少使用。
链接属性可以被修改,当然存储类型也可以被修改,static这个关键字就可以将自动变量修改成静态变量,使得被修饰的变量生命周期边长。程序执行的过程中一直存在。不会随着部分函数的销毁而销毁
来看如下代码,你觉得结果如何?
其运行结果如下
可见函数sum()调用一次销毁后,使用static修饰的变量b还没有被销毁,当第二次调用的时候还保留着第一次调用后的值。修改了这个变量的生命周期。
当然static关键字在不同的上下文环境,其所表达的意思也不一样:
在代码块内部使用,static用于修改变量存储类型
在代码块外面使用,或者函数定义时static用于修改链接属性。
关于register
这个关键字,它用于声明自动变量,建议这个变量存储在寄存器中,因此这类变量称为寄存器变量,但是需要注意的是,只是建议!建议!建议!,编译器并不一定就会把这个变量存储在寄存器中,如果编译器认为这个变量存储在寄存器中比人脑壳更好,那么它就可能会采取建议。
关于这三个变量属性的总结如下