C Primer Plus——3.存储类、链接和内存管理

  1. 链接
    变量具有一种链接,分为外部链接( external linkage)、内部链接(internal linkage)、空链接(no linkage)
    具有文件作用域的变量可能有外部链接或内部链接,具有块作用域或函数原型作用域的有空链接。外部链接表示可以在多个文件程序的任何地方使用,内部链接表示可以在一个文件的任何地方使用,通过关键字static区分!
int giant = 5; // external, file scope
static int dodgers = 3; // internal, file scope
int main(){...}

giant可以在属于同一程序的其他文件中使用,但加static只能在本文件中使用

  1. 存储时期
    变量具有一种存储时期,分为静态存储时期、自动存储时期。if 一个变量具有静态存储时期,它在程序执行期间会一直存在,要用常量来初始化。文件作用域的变量具有静态存储时期,static关键字表明链接类型而不是存储时期!自动存储期表示程序进入定义这些变量的代码块时为变量分配内存,退出时释放内存。

C使用作用域、链接、存储时期组合起来定义了5中存储类:自动、寄存器、具有代码块作用域的静态、具有外部链接的静态、具有内部链接的静态

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

自动变量可显式地加auto

if luckily, 寄存器变量可以存到寄存器或高速内存中,这取决于编译器根据当时的情况是否给你寄存器使用。需要显式地使用register关键字.

具有file scope的变量一定是静态存储的。代码块里的变量也可以静态但需要显式static一下,函数结束后这种变量还会存在,下次调用会延续值,静态变量不初始化的话,则会系统为它赋0
函数参数不能用static

void fun(static int n); // illegal
  1. 具有外部链接的静态变量
    在函数外定义,定义的时候不加关键字。如果声明一个定义在别的文件中的外部链接静态变量就需要加extern关键字!
int Errupt; 
double Up[100];
extern char Coal; // 这个Coal定义在别的文件中了
void next(void);
int main(){
  extern int Errupt; // 可以不写,写表示我这里会用到
  extern double Up[]; // 可以不写,数组大小不写也可,因为定义过了
  ...}

如果在main中用了int Errupt就会覆盖静态变量Errupt,使代码块的变量在main中起作用instead of 全局变量errupt
外部变量初始化
只能用常量表达式,不能=2*x这样,可以=sizeof (int)

  1. 具有内部链接的静态变量
    在函数外使用static定义
static int volume = 1;
int main(){...}

内部链接决定它只在本文件中使用
extern只是用来声明文件作用域的变量,不会改变变量的链接,所以可以用在内部链接的静态变量上

int traveler = 1;
static int stayhome = 1;
int main(){
extern int traveler;
extern int stayhome;
  1. 存储类说明符
    5个:auto, register, static, extern, typedef
    register的变量不能使用地址。请求将变量存到寄存器中
    static说明符对具有代码块作用域的变量声明时,使它具有静态存储时期。对具有文件作用域的变量声明时,使它具有内部链接
    extern说明符表明已经在别处定义了这个变量

函数也有存储类之分,默认情况下函数是外部的,可以是静态的,决定了其他文件能不能调用它们。

double gamma(); // 默认为extern的
static double beta();
extern double delta(); // 其他文件定义了delta,现在来声明的

使用static主要为了创建一个模块所私有的函数,避免命名冲突

随机函数和静态变量
ANSI C的随机函数是可移植的,在不同机器上有确定的随机序列。(编译器有内置的随机函数rand(),需要头文件stdlib.h)
rand()产生的是0到RAND_MAX范围内的整数,可以取模来产生小范围
引入自己写的头文件的时候,用""告诉编译器在本地寻找头文件而不是在标准头文件的地方寻找。引入头文件,那里有声明函数和变量,于是主函数那个文件就不用声明了

malloc() & free(),需要头文件stdlib.h
malloc的参数是所需内存字节数,返回指向第一个字节的地址,如果找不到空间就返回NULL

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

其中cast部分 (double *)在C是可选的在C++是必须的
申请了30个double的空间,由ptd指向第一个double空间, 可以用ptd[index]
于是创建数组的三个方法为:(1)声明时用常量表达式指定维度(2)用变量表达式声明变长数组(3)声明个指针,用malloc
free()的参数是一个malloc返回的指针,不能用free来释放数组分配的内存

calloc()

long * newmem;
newmem = (long *)calloc(100, sizeof(long)); 

calloc有2个参数,第一个是单元格数量,第二个是单元格大小。需要cast
calloc会把内存块的比特位都置为0,也不知道有什么影响
free() 可以释放calloc分配的内存

变长数组是自动存储的,不需要手动释放。动态内存分配需要free
malloc创建的数组可以让别的函数使用,只要返回指针供别的函数使用,使用之后可以free,free的地址一样就可以不必要同一个指针

变量由类型和存储类表征。C90增加了两个属性:不变性和易变性,由关键字const, volatile声明,C99增加了restrict来方便编译,这三个是受限类型。

类型限定词const
关键字const使变量不可通过赋值、自增自减来修改

const int price;
price = 12; // illegal

const int price = 12; // legal
const int factor[3] = {0.8, 0.6, 0.5}; // legal

在指针和参量中使用const

const float * pf; // 指向的值是不变的,但pf可变可以指向别的值
float const * pf; // 同上
float * const pf; // 指针不可变,总是指向同一个地址
const float * const pf; // 都不可变
// const在*左边的都是不可修改指向的值,在右边是不能修改指针

为防止函数传递数组时,被误修改数组的值,可以用const修饰参数

void display(const int array[], int limit);
void display(const int * array, int limit); // equal

对全局变量使用const
全局变量会暴露数据,在程序任何地方都可以修改。如果加上const就可以了。在文件之间共享数据的方法:
(1)在一个文件中定义,在另一个中声明
(2)在头文件中定义,其他文件中引入

//file.h
static const double PI = 3.14159;

如果不加static会在每个包含了头文件的文件中都有同一表示符的定义声明,ANSI不支持这样。加上static使每个文件有了一个数据拷贝。缺点是复制数据,如果常量是很大的数组就有点问题。

类型限定词volatile
volatile告诉编译器,这个变量可能被其他代理改变除了这个程序外。语法同const。没有const,说明这个变量不会被其他并行程序更改,就可以让编译器进行代码优化(比如把变量放到cache中)。一个变量可以同时有const 和volatile,前后顺序不重要,程序不修改它,但是程序以外的代理可能修改。

  1. restrict
    也是提供编译器优化的,restrict只修饰指针,且这个指针是访问数据的唯一且初始的方式,所以指针对数据的多次操作可以合并(优化). restrict修饰的指针是指向那个区域的唯一的指针
int * restrict ptr = (int *)malloc(10*sizeof(int));
void * memcpy (void * restrict s1, const void * restrict s2, size_t n); // void指针可以cast成其他类型

C99新语法

void f(int * const a1, int * restrict a2, int n); //old
void f(int a1[const], int a2[restrict], int n); // new
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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