第十二章 存储类别、链接和内存管理
12.1 存储类别
对象(object):一块存储数据的内存,可以存储一个或多个值。
标识符(identifier):标识符是一个名称(变量名),用来指定对象的内容。遵循变的量命名规则。
可以认为对象是硬件层面,标识符是软件层面。
标识符不是唯一指定对象的途径。
int *pt = &entity;
int ranks[10];
*pt
不是标识符,但它指定了一个对象,即能够访问内存。
左值:指定对象的表达式。
可修改的左值:可以使用左值改变对象中的值的左值。
const char *pc = "Behold a string literal!";
pc
是可修改的左值;
*pc
是不可修改的左值。
12.1.1 作用域(scope)
描述程序中可访问标识符的区域。
-
块作用域(block scope):块是用一对花括号括起来的代码区域,定义在块内的变量具有块作用域,块作用域变量的可见范围是从定义处到包含带定义的块的末尾。另外,虽然函数的形式参数在函数的左花括号之前,但是它们也具有块作用域,属于函数体这个块。类似的还有
for while
循环、do while
循环和if
语句。 -
函数作用域(function scope):整个函数,仅用于
goto
语句的标签。即使一个标签的首次出现在函数的内层块中,它的作用域也延伸至整个函数。 - 函数原型作用域(function prototype scope):从形参定义到原型声明结束,用于函数原型的形参名,但是函数原型的形参名通常无关紧要。
- 文件作用域(file scope):从变量定义到定义所在文件的末尾,文件作用域变量亦称全局变量。
12.1.2 链接(linkage)
标识符的多个翻译单元间的属性,是否跨翻译单元访问。
-
外部链接:文件作用域的非
static
变量。 -
内连接:文件作用域的
static
变量。 - 无连接:具有块作用域、函数作用域或函数原型作用域的变量都是无连接变量。意味着这些变量属于定义它们的块、函数或原型私有。
12.1.1 存储期(storage duration)
对象在内存中保留的时间。
-
静态存储期:程序执行阶段一直存在。文件作用域变量,
static
变量。 - 线程存储期:从声明到线程结束一直存在。并发程序设计时考虑。
-
自动存储期:程序运行到作用域内到出该变量的作用域结束。除
static
变量的块作用域变量。变长数组不同,它们的存储周期从声明到出该变量的作用域结束。 -
动态分配存储期:
malloc
到free
结束。
5
中存储类别
存储类别 | 存储期 | 作用域 | 链接 | 声明方式 |
---|---|---|---|---|
自动 | 自动 | 块 | 无 | 块内 |
寄存器 | 自动 | 块 | 无 | 块内,使用关键字 register
|
静态外部链接 | 静态 | 文件 | 外部 | 所有函数 |
静态内部链接 | 静态 | 文件 | 内部 | 所有函数外,使用关键字 static
|
静态无链接 | 静态 | 块 | 无 | 块内,使用关键字 static
|
不建议用
auto
关键字显示声明为自动变量。与C++
会有兼容性问题。如果变量同名,内层块中的变量会隐藏外层块的定义。尽量避免同名,以免混淆。
静态的意思是其在内存中的位置不变,而非值不变。
寄存器变量:register
修饰,尽量变量存储在 CPU
寄存器中,取决于编译器。不能对其使用地址运算。
引用式声明(referencing declaration):extern
关键字修饰表明该声明不是定义,指示编译器去别处查询其定义。函数默认为外部函数。
12.2 随机函数和静态变量
简单实现了一个随机数种子生成函数和随机数产生函数,没什么好记的。
12.3 掷骰子
用 C
库函数 sand()
实现
12.4 分配内存:malloc() 和 free()
动态内存与静态内存的区别相当于一群人去饭店吃饭。
- 动态内存就是吃一碗点一碗,不够再加,处理麻烦;
- 静态内存就是估算每个人的饭量一次点完,可能会出现有人挨饿或剩饭的情况。
12.4 ANSI C 类型限定符
const:修饰常量不可变,注意修饰指针时就顶层 const
和底层 const
之分
volatile:告诉计算机该变量会在程序之外被修改,同时编译器不会优化含有该变量的相关代码,因为其随时会被修改。通常用于硬件地址以及在其他程序或同时运行的线程中共享数据。
restrict(C99):只能修饰指针,表明该指针时访问数据对象的唯一且初始的方式,允许编译器更好地优化代码。restrict
关键字有两个读者:
- 一个是编译器,告知编译器可以自由假定一些优化方案;
- 另一个是用户,告知用户要使用满足
restrict
要求的参数。
_Atomic(C11):并发程序设计中,当一个线程对一个原子类型的对象执行原子操作时,其他线程不能访问该对象。
小知识
static
除了表明存储类别变量的作用域或链接外,还有新的用法:告诉编译器如何使用形式参数(C99
) 。double stick(double ar[static 20])
表示
ar
数组至少有 20 个元素,目的时让编译器使用这些信息优化函数代码。