第12章 存储类别、链接和内存管理
- 存储期是指对象在内存中保留了多长时间
- 标识符用于访问对象,可以用作用域和链接描述标识符,标识符的作用域和链接表明了程序的哪些部分可以使用它
- 不同的存储类别具有不同的存储期、作用域和链接
- 标识符可以在源代码的多文件中共享、可用于特定文件的任意函数、可仅限于特定函数中使用、甚至只在函数中的某部分使用
- 对象可存在于程序的执行期,也可以仅存在于它所在函数的执行期
- 作用域描述程序中可访问标识符的区域。一个C变量的作用域可以是块作用域、函数作用域、函数原型作用域或文件作用域
- 定义在块中的变量具有块作用域,块作用域变量的可见范围是从定义处到包含该定义的块的末尾
- C99标准允许在块中的任意位置声明变量
- 变量定义在函数的外面,具有文件作用域,具有文件作用域的变量,从它的定义处到该定义所在文件的末尾均可见
- 文件作用域变量也成为全局变量
- 编译器把源代码文件和所有的头文件都看成是一个包含信息的单独文件,这个文件被称为翻译单元,描述一个具有头文件作用域的变量时,它的实际可见范围是整个翻译单元,如果程序由多个源代码文件组成,那么该程序也将由多个翻译单元组成,每个翻译单元均对应一个源代码文件和它所包含的文件
- C变量有3中链接属性:外部链接、内部链接或无连接
- 具有块作用域、函数作用域和函数原型作用域的变量都是无链接变量,这说明这些变量属于定义它们的块、函数或函数原型私有
- 具有文件作用域的变量可以是外部链接或内部链接,外部链接变量可以在多文件程序中使用,内部链接变量只能在一个翻译单元中使用。
- 类别存储说明符
static
。使用static
的文件作用域变量是内部链接,不适用的为外部链接。 - 作用域和链接描述了标识符的可见性。存储期描述了通过这些标识符访问的对象的生存期
- C对象有4种存储期:静态存储期、线程存储期、自动存储期、动态分配存储期
- 如果对象具有静态存储期,则它在程序的执行期间一直存在。文件作用域变量具有静态存储期。对于文件作用域变量,关键字
static
表明了其链接属性,而非存储期。不管是内部链接或外部链接,所有的文件作用域变量都具有静态存储期。 - 线程存储期用于并发程序设计,具有线程存储期的对象,从被声明时到线程结束一直存在
- 块作用域的变量通常都具有自动存储,当程序进入定义这些变量的块时,为这些变量分配内存,当退出这个块时,释放刚才为变量分配的内存。这种做法相当于把自动变量占用的内存视为一个可重复使用的工作区或暂存区
- 块作用域变量大多为自动存储期,但也可以具有静态存储期,此时使用
static
声明 - 属于自动存储类别的变量具有自动存储期、块作用域且无链接。
- 默认情况下,声明在块或函数头中的任何变量都属于自动存储类别。
- 显式的声明自动变量,可以显式使用关键字
auto
。关键字auto
是存储类别说明符 - 块作用域和无链接意味着只有在变量定义所在的块中才能通过变量名访问该变量
- 变量具有自动存储期意味着,程序在进入该变量声明所在的块时变量存在,程序在退出该块时变量消失。
- 如果内层块中声明的变量与外层块中的变量同名,则内层块会隐藏外层块的定义,在离开内层块后,外层块变量的作用域又会回到原来的作用域
- C99中,作为循环或
if
语句的一部分,即使不使用花括号,也是一个块 - 自动变量不会初始化,除非显式初始化它
- 变量通常存储在计算机内存中,寄存器变量存储在CPU的寄存器中。无法获取寄存器变量的地址。
- 绝大多数情况下,寄存器变量是块作用域、无链接和自动存储期
- 使用存储类别说明符
register
可声明寄存器变量 - 对于寄存器变量,编译器必须根据寄存器或最快可用内存的数量衡量请求,或者直接忽略请求。如果被忽略,则寄存器变量就变成普通的自动变量。不管什么情况下,仍旧不能对寄存器变量使用地址运算符。
- 静态变量中静态的意思是该变量在内存中原地不动,其值可变
- 不能在函数的形参中使用
static
- 外部链接的静态变量具有文件作用域、外部链接和静态存储期
- 如果一个源代码文件使用的外部变量定义在另一个源代码文件中,则必须使用
extern
在该文件中声明该变量 - 如果一个函数使用了外部变量,则为指出这点,可以在函数中用关键字
extern
再次声明(可选) - 与自动变量不同,如果未初始化外部变量,它们会被自动初始化为0
- 定义式声明,引用式声明
- 不要使用关键字
extern
创建外部定义,只能用它来引用现有的外部定义 - 外部变量只能初始化一次,且必须在定义该变量时进行
- 内部链接的静态变量具有静态存储期、文件作用域和内部链接
- 外部链接的静态变量可用于同一程序中任意文件中的函数,内部链接的静态变量只能用于同一个文件中的函数。
- 注意:如果外部变量定义在一个文件中,则其他文件在使用该变量之前必须先声明它(使用
extern
关键字)。即在某文件中对外部变量进行定义式声明只是单方面允许其他文件使用该变量,其他文件在用extern
声明之前不能直接使用它 - 函数也有存储类别,可以时外部函数或静态函数。外部函数可以被其他文件的函数访问,静态函数只能用于其定义所在的文件。
- 将文件名放在双引号而不是尖括号中,指示编译器在本地查找文件,而不是到编译器存放标准头文件的位置去查找文件
- 一个变量只能有一个定义式声明,但是带
extern
的声明是引用式声明,可以有多个引用式声明 malloc()
函数:该函数接受一个参数(所需要的内存字节数),malloc()
函数会找到合适的空闲内存块,这样的内存是匿名的。该函数分配内存,但是不会为其赋名,且会返回动态分配内存块的首字节地址malloc()
要和free()
配合使用malloc()
可能分配不到所需的内存,在这种情况下,该函数返回空指针,程序结束。- 静态内存的数量在编译时是固定的,在程序运行期间也不会改变。自动变量使用的内存数量在程序执行期间自动增加或减少。但是动态分配的内存数量只会增加,除非使用``free()```进行释放
- 程序把静态对象、自动对象和动态分配的对象存储在不同的区域
- 以
const
关键字声明的对象,其值不能通过赋值或递增、递减来修改 const
关键字的常见用法是声明为函数形参的指针- 如果一个指针仅用于给函数访问值,应将其声明为一个指向
const
限定类型的指针。如果要用指针更改主调函数中的数据,就不使用const
关键字 - 在文件之间共享
const
数据的两个策略,第一,遵循外部变量的常用规则,即在一个文件中使用定义式声明,在其他文件中使用引用式声明(用extern
关键字);第二,把const
变量放在一个头文件中,然后在其他文件中包含该头文件,这种方法必须在头文件中用关键字static
声明全局const
变量