存储类、链接和内存管理

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 storage duration)
  • 自动存储时期(automatic storage duration)

静态存储时期

具有文件作用域的变量具有静态储存时期,不管是内部链接的文件作用域还是外部链接的文件作用域,都具有静态储存时期,静态储存时期的变量在程序的执行期间将一直存在。关键词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(),然后使用该指针访问数组元素。

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。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容