C Primer Plus,十二

12.1 存储类

存储时期:变量在内存中保存的时间。变量的作用域和链接一起表明程序的哪些部分可以通过变量名来使用该变量。

不同的存储类提供了变量的作用域、链接以及存储时期的不同组合。

12.1.1 作用域

作用域描述了程序中可以访问一个标识符的一个或多个区域。一个C变量的作用域可以是代码块作用域、函数原型作用域,或者文件作用域。

在代码块中定义的变量具有代码块作用域,从该变量被定义的地方到包含该定义的代码块的末尾该变量均可见。另外,函数的形式参量尽管在函数的开始花括号前进行定义,同样也具有代码块作用域,隶属于包含函数体的代码块。

传统上,具有代码块作用域的变量都必须在代码块的开始处进行声明。C99放宽了这一限制,允许在一个代码块中任何位置声明变量。

函数原型作用域从变量定义处一直到原型声明的末尾。这意味着编译器在处理一个函数原型的参数时,它所关心的只是该参数的类型,而不关注参数名字。(除VLA外)

一个所有函数之外定义的变量具有文件作用域,它从定义处到包含该定义的文件尾都是可见的。

文件作用域也被称为全局变量。

另外还有一种被称为函数作用域的作用域,但它只适用于goto语句使用的标签。

12.1.2 链接

一个C变量具有下列链接之一:外部链接、内部链接或空链接。

具有代码块作用域或者函数原型作用域的变量具有空链接,意味着它们是由其定义所在的代码块或函数原型所私有的。

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

一个具有外部链接的变量可以在一个多文件程序的任何地方使用。

一个具有内部链接的变量可以在一个文件的任何地方使用。

如果在外部定义中使用了存储类说明符static则是内部链接。

12.1.3 存储时期

一个C变量具有以下两种存储时期之一:静态存储时期和自动存储时期。

如果一个变量具有静态存储时期,它在程序执行期间将一直存在。具有文件作用域的变量具有静态存储时期。

所有的文件作用域变量,无论它具有内部链接,还是具有外部链接,都具有静态存储时期。

static表明链接类型,并非存储时期。

具有代码块作用域的变量和一般情况下具有自动存储时期。该思想把自动变量使用的内存视为一个可以重复使用的工作区或者暂存内存。

12.1.4 自动变量

属于自动存储类的变量具有自动存储时期、代码块作用域和空链接。

默认情况下,在代码块作用域或函数的头部定义的任意变量都属于自动存储类。也可以通过显示的使用关键字auto使您的这个意图更清晰。

语句若为循环或者if语句的一部分,即使没有使用{},也认为是一个代码块。

12.1.5 寄存器变量

因为寄存器变量多是存放在一个寄存器而非内存中,所以无法获得寄存器变量的地址。具有自动存储时期、代码块作用域和空链接。通过显示的使用关键字register可以声明寄存器变量。

声明一个寄存器类变量仅是一个请求,而非一条直接的命令。所以可能达不成目的,而成为一个普通的自动变量。

可以使用register声明的类型是有限的。例如,处理器可能没有足够大的寄存器来容纳double类型。

12.1.6 具有代码块作用域的静态变量

静态指的是变量的位置固定不变。具有文件作用域的变量自动具有静态存储时期。可以通过使用存储说明符static在代码块内声明创建具有代码块作用域的静态变量。

静态变量和外部变量在程序调入内存时已经就位了。

对函数参量不能使用static

12.1.7 具有外部链接的静态变量

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

把变量的定义声明放在所有函数之外,即创建了一个外部变量。为了程序更加清晰,可以使用extern关键字来再次声明它。如果变量是在别的文件中定义的,使用extern来声明改变量是必须的。

如果在函数的声明中漏掉了extern,就会建立一个独立的自动变量。

外部变量的作用域:从声明的位置开始到文件结尾为止。

和自动变量一样,外部变量可以被显示地初始化。不同于自动变量的是,如果您不对外部变量进行初始化,它们被自动赋值0。

不同于自动变量,只可以用常量表达式来初始化文件作用域变量。

int tern=1;

main()

{

external int tern;

第一次声明tern称为定义声明,第二次声明称为引用声明。

不要用关键字extern来进行外部定义,只用它来引用一个已经存在的外部定义。

一个外部变量只可进行一次初始化,而且一定是在变量被定义时进行的。

extern char permis='u'; //错误

12.1.8 具有内部链接的静态变量

具有内部链接的静态变量具有文件作用域、内部链接和静态存储时期。通过使用存储类说明符static在所有函数外部进行定义。

它只可以被与它在同一个文件中的函数使用。

12.2 存储类说明符

C语言中有5个作为存储类说明符的关键字,它们是auto、register、static、extern以及typedf。typedf与内存存储无关,由于语法原因被归入此类。不可以在一个声明者使用一个以上存储类说明符。

auto:一个变量具有自动存储时期,该说明符只能用在具有代码块作用域的变量声明中。

register:也只能用在具有代码块作用域的变量声明中,它的使用也使您不能获得变量的地址。

static:用于具有代码块作用域的变量声明时,使该变量具有静态存储时期,从而得以在程序运行期间存在并保留其值。用于文件作用域的变量声明时,表明该变量具有内部链接。

extern:表明您在声明一个已经在别处定义了的变量。

12.3 存储类和函数

函数也具有存储类,可以是外部的或者静态的。外部函数可以被其它文件中的函数调用,而静态函数只可以在定义它的文件中使用。

12.4 分配内存:malloc()和free()

malloc():它接受一个参数:所需内存字节数,然后malloc()找到可用内存中一个大小合适的块,返回那块内存第一个字节的地址,如果找不到所需空间,返回空指针。

double * ptd;

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

这段代码请求30个double类型值的空间,并且把ptd指向该空间锁在位置。

现在创建数组有三种方法

1、声明一个数组,用常量表达式指定数组维数。

2、声明一个变长数组,声明时用变量表达式指定数组维数。

3、声明一个指针,调用malloc()。

一般地,对应每个malloc()调用,应该调用一次free()。函数free()的参数是先前malloc()返回的地址,不能使用free()来释放通过其它方式分配的内存。

在头文件stdlib.h有malloc()和free()的原型。

标准库提供了两个保证能够在所有操作系统下工作的返回值:EXIT_FAILURE指示程序异常终止,EXIT_SUCCESS指示程序正常终止。

12.4.1 free()的重要性

在编译程序时,静态变量的数量是固定的,在程序运行时也不改变。自动变量使用的内存数量在程序执行时自动增加或减少,但被分配的内存所使用内存数量只会增加,除非您记得使用free()。否则可能造成内存泄露。

12.4.2 函数calloc()

例子如下:

long * newmem;

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

函数calloc()还有一个特性:它将块中的全部位都置为0。

函数free()也可以用来释放由calloc()分配的内存。

12.4.3 动态内存分配与变长数组

VLA与malloc()都可以用来创建一个大小在运行时决定的数组。

区别在于VLA是自动存储的,即VLA所用内存空间在运行完定义部分之后会自动释放。

使用由malloc()创建的数组不必局限在一个函数中。

自动变量:将这一部分内存处理为一个堆栈,这意味着在内存中,新变量在创建时按顺序加入,在消亡时按相反顺序移除。

使用动态内存往往导致进程比使用堆栈内存慢。

12.5 ANSI C的类型限定词

C99授予类型限定词一个新属性:它们现在是幂等的。这意味着可以在一个声明中不止一次地使用同一限定词,多余的将被忽略掉。

const const const int n=6;相当于const int n=6;

如果变量声明中带有关键字const,则不能通过赋值、增量或减量运算符来修改值。

12.5.1 类型限定词volatile

限定词volatile告诉编译器该变量除了可被程序改变以外还可被其他代理改变。典型的,它被用于硬件地址和与其它并行运行的程序共享的数据。

一个值可以同时是const和volatile。程序不能改变它的值,这一点使它成为const,但它被程序以外的代理改变,这使它成为volatile。

12.5.1 类型限定词restrict

关键字restrict只可用于指针,并表明指针是访问一个数据对象的惟一且初始的方式。

int ar[10];

int *restrict restar=(int *)malloc(10 *sizeof(int));

int *par=ar;

指针restar是访问由malloc()分配的内存的惟一且初始的方式,因此它可以使用这个关键字。

par指针既不是初始的,也不是访问数组ar中数据的惟一方式,因此不可以使用。

void * memcpy(void * restrict s1,void * restrict s2,size_t n);

把s1,s2声明为restrict意味着每个指针都是相应数据的惟一访问方式,因此它们不能访问同一数据块,即不能重叠。