存储类、链接和内存管理

2018-12-22  本文已影响0人  nytta

C语言为变提供了5种不同的存储模型,或称存储类,还有基于指针的第6种存储模型。可以按照一个变量(或者说一个数据对象)的存储时期(storage duration)描述它,也可以按照它的作用域(scope)以及它的链接(linkage)来描述它。

1.作用域

作用域可以是

代码块作用域

一个函数体是一个代码块,或者函数体内的一个复合语句也一个代码块,另外,函数的形式参量尽管在函数的开始花括号前进行定义,同样也具有代码块作用域,属于包含函数体的代码块。

double blocky(double cleo)
 {
double p = 0.0;
//...
return p;
 }

变量cleo和p都具有代码块作用域。

函数原型作用域

function prototype scope,适用于声明函数原型中使用的变量名。
int mighty(int mouse, double large);
函数原型作用域从变量定义处一直到原型声明的末尾,意味着编译器在处理函数原型时,只关心参数的类型,并不关心参数名,这就解释通了,在函数原型的声明中,可以省略参数名。

文件作用域

在函数之外定义的变量,具有文件作用域,文件作用域的变量从它定义之处开始到文件的结尾都是可见的。

 #include <stdio.h>
 int myvar = 0;  /* 具有文件作用域的变量 */

 void main(void)
{
//...
}

2.链接

C语言中变量有

空链接

具有代码块作用域或者函数原型作用域的变量具有空链接,空链接的变量在定义的代码块内或函数原型私有的,其他部分的代码访问不了这个变量。

外部链接或内部链接

具有文件作用域的变量具有外部链接,或者具有内部链接。

#include <stdio.h>

int giants = 5;            // 文件作用域,具有外部链接
static int dodgers = 1;    // 文件作用域,具有内部链接

void main(void)
{
//...
}

外部链接,不仅在本文件内可见,而且在其他文件也是可见的;内部链接只能在本文件内可见,是本文件私有的,使用关键词static修饰的文件作用变量,具有内部链接。

3.存储时期

C语言有两个存储时期

静态存储时期

具有文件作用域的变量具有静态储存时期,不管是内部链接的文件作用域还是外部链接的文件作用域,都具有静态储存时期,静态储存时期的变量在程序的执行期间将一直存在。关键词static修饰的变量是指内部链接,不是用于指静态存储时期的作用。

自动存储时期

具有代码块作用域的变量一般情况下具有自动存储时期。在程序进入代码块时,程序会为这些变量分配内存,当退出相应的代码块时,分配的内存会被释放。

C语言使用作用域、链接和存储时期来定义5种存储类:

5种存储类

存储类 时期 作用域 链接 声明方式
自动 自动 代码块 代码块内
寄存器 自动 代码块 代码块内,使用字关键字register
具有外部链接的静态 静态 文件 外部 所有函数之外
具有内部链接的静态 静态 文件 外部 所有函数之外,使用关键字static
空链接的静态 静态 代码块 代码块内,使用关键字static

寄存器变量

一般来说,变量是存储中计算机内存中,不过,寄存器变量可以被存储在CPU寄存器中,这样,存储在速度最快的可用内存中,从而可以比普通变量更快地被访问和操作。
由于寄存器变量多是存放在一个寄存器而不是内存中,所以无法获得寄存器变量的地址,不过在其他的许多方面,寄存器变量与自动变量是一样的,也就是说,它们都有代码块作用域,空链接,自动存储时期,一般使用存储类说明符register来声明寄存器变量;

 int main(void)
 {
register int quick;
//...
 }

或者形式参数也可以为寄存器变量:
void macho(register int m)
可以使用register声明的寄存器变量的类型是有限的,例如,处理器可能没有足够大的寄存器来容纳double类型。

具有外部链接的静态变量

具有外部链接的静态变量具有文件作用域,外部链接和静态存储时期,如:

int a;                   /* 定义外部链接的静态变量 */
double da[100];          /* 定义外部链接的静态变量 */
extern char c;           /* 由于变量c已经在其他文件定义过,所以在这里再定义变量c,*/
                     /* 其实是引用其他文件定义的外部链接静态变量c,所以必须加extern   关键词修饰 */

void main(void)
{
extern int a;        /* extern说明是引用了外部链接的静态变量a */

double da[100];      /* 没有extern关键词修饰,则定义的是局部变量,代码块作用域的自动变量 */
                     /* 在代码块作用域内,默认是自动变量 */

auto char c;         /* 或者使用auto关键词显式地声明变量c为自动变量 */      
}

存储类和函数

函数也有具有存储类。函数默认情况下是外部,当然也可以声明为静态函数,外部函数可以被其他文件的函数调用,而静态函数则只能在定义它的文件中使用。

double gamma();           /* 默认声明的函数是外部函数 */
static double release(); 
extern dbouel delele();

函数gamma()和delele()可以被其他文件的函数使用,而函数release()则只能在本文件内使用。
注意,函数delele()声明时使用extern关键词修改,并不是表明它是引用其他文件的函数,而是显式表明delele()是外部函数,可以被其他文件使用。

分配内存

主要函数malloc()和free()

函数malloc()可以用来返回数组指针、结构指针等等,因为一般需要把返回值的类型指派为适当的类型。

double * ptd;
ptd = (double *) malloc(30 * sizeof(double));

这段代码返回请求30个double类型值的空间,并且把ptd指向该空间所在位置。
创建一个数组有3种方法:

malloc()分配内存失败时没有足够的内存,会返回NULL。

一般地,对应每个malloc(),应用要调用一次free()释放内存。

free(ptd);

free()方法使用指针为入参。

类型限定词

volatile

限定词volatile告诉编译器该变量除了可被程序改变以外还可被其他代理改变。典型地,它被用于硬件地址和与其他并行运行的程序共享的数据。
例如,一个地址中可能保存着当前的时钟时间,不管程序做些什么,一眼该地址的值都会随着时间而改变。

volatile int locl;       /* locl是一个易变的位置 */   
volatile int * ploc;     /* ploc指向一个易变的位置 */

其实volatile可以方便编译器优化:

val1 = x;
//...other code...
val2 = x;

编译器注意到程序再次使用了x,而没有改变它的值,编译器会把x临时存储在一个寄存器中,当val2需要x时,可以通过从寄存器而不是从内存位置中读取值,这样可以更快地节省时间,这个过程被称为缓存。

restrict

关键字restrict通过允许编译器优化某几种代码增强了计算支持。它只可用于指针,并表明指针是访问一个数据对象的唯一且初始的方式。

int ar[10];
int * restrict restar = (int *)malloc(10 * sizeof(int));
int * par = ar;

这里,指针restar是访问由malloc()分配的内存的唯一且初始的方式,因此,它可以由关键字restrict限定,而par既不是初始的,也不是访问数组ar中数据的唯一方式,因此不能把par限定为restrict。

上一篇 下一篇

猜你喜欢

热点阅读