C的存储类,链接与内存管理(Storage Class, Linkage, Memory Management)

C中,object指一块内存区域,一个object可以存储一或多个值。一个object可能还未存储任何值,但是它有存储一个恰当值的合适大小。"int entity =3;"声明了一个叫做entityidentifieridentifier是一个名字,它指定一个具体object的内容,这个identifier就是C程序指定存储在硬件内存中object的方式。变量名并不是指定一个object的唯一方式,例如"int* p = &entity;"中,p就是一个identifier;这里*p不是identifier因为它不是一个名字(虽然它也指定了一个object)。对数组来说,数组整体是一个object,同时其中的每一个元素也是object

C预处理器在处理#include "*.h"时,会将所包含的头文件的内容放在include处,所以虽然有多个文件,编译器只看到了一个文件,这一个文件就叫translation unit


只能用常量表达式来初始化file scope的变量(只要不是变长数组,sizeof表达式也被认为是常量)



Linkage(链接):

1. external linkageexternal linkage的变量可用在一个多文件的程序中的任意位置

2. internal linkageinternal linkage的变量可以用在一个translation unit中的任何位置

3. no linkage:block scope, function scopefunction prototype scope的变量是no linkage,意味着它们只是他们被定义的block, functionfunction prototype的私有变量

file scope的变量可以是internal linkageexternal linkage。通常"file scope with internal linkage"变量被称为"file scope"变量,而"file scope with external linkage"的变量被称为"global scope""program scope"file scope变量默认是external linkage,要想将其设为internal linkage需要在声明变量时加上"static"

scopelinkage描述了identifier的可见性



Storage Duration

描述了object可以被identifier访问的持久性

1. static storage duration:在整个程序执行过程中一直存在。file scope变量是static storage duration。用"static"声明的file scope变量有internal linkage,但所有的file scope变量,无论是使用internal linkage还是external linkage都有static storage duration;注意,这里关键词"static"表明的是变量的linkage类型,而非storage类型static内存的使用量是在compile时决定的,static数据和程序一起同时被载入内存。static的变量不被存放在stack中,他们被存储在data segment中。

如果static变量未初始化,被放在uninitialized data segment中;如果已初始化,则存放在initialized data segment中(所有的gloabl,static和constant data都被存储在这个区域)。注意,stack,heap还有data segment都是在memory中的,只不过划分了不同的区域 

2. thread storage duration:从被声明时开始,存在到thread结束。这种object的声明方法与file scope相同,但是要加上关键字"_Thread_local",当有这个关键字时,每个线程都会有一份这个变量的私有副本

3. automatic storage duration:当程序进入automatic storage duration的变量被定义的block时,这些变量会被分配存储空间,当结束block时内存被释放。block scope的变量通常具有automatic storage duration,但也可以通过添加关键词"static"将其变成static storage duration,此时它们具有block scopeno linkagestatic storage durationVLA有一点不同是他们从被声明时才开始存在

4. allocated storage duration

由程序员决定变量的durationmalloc()- free()



Storage Class

1. automatic

属于automatic storage class的变量有automatic storage durationblock scopeno linkage。任何在blockfunction中定义的变量默认都是automatic storage duration,但可以通过添加关键词"auto"显式声明(但是由于C++"auto"的含义完全不同,所以为了C/C++兼容性最好不要使用"auto")。"auto"只能用于block scope变量的声明,而block scope的变量已经是automatic storage duration了,所以它的主要目的是提醒意图

数组是automatic storage class,所有automatic storage class的变量都是被放在stack中的

2. register

通常变量是存储在计算机内存中的,但register变量存储在CPU register中(最快的可用存储区域)。但是由于变量是存储在register中的,所以无法取得register变量的地址。其他方面register变量与automatic变量一样,有block scopeno linkageautomatic storage duration。要声明一个register变量需要关键词"register",但即便使用了关键词也不一定能获得一个register变量,compiler会决定是否给予一个register变量,如果compiler忽略了这个请求,将只能获得一个普通automatic变量,但依然无法使用地址操作符来处理它

3. block scopestatic变量(static variables with block scope)(static with no linkage

这里的static指变量一直存放于内存中(即static storage duration),其值可能改变,但是no linkage(file scope的变量自动获得static storage duration)。这种变量只能被初始化一次,如果没有显式初始化则会被初始化为0

例:

for(int i = 0; i < 3; i++)
    trystat();
...
void trystat(void)
{
    int fade = 1;
    static int stay = 1;
    printf("fade= %d, stay = %d\n", fade++, stay++);
}

结果为:

fade= 1, stay = 1

fade= 1, stay = 2

fade= 1, stay = 3

因为"stay"被声明为static storage duration,所以在程序运行过程中一直存在,而不像"fade"被声明又被销毁;同时"stay"只会被初始化一次,即在trystat()compile时。如果不显式的将其初始化,则它会自动被初始化为0。其实"staticint stay =1;"并不是trystat()函数的一部分,static变量和external变量在程序被载入内存时已经存在了,将声明语句放在trystat()函数中只是告诉compiler这个变量只有trystat()函数可见,而不是一个在程序运行中被执行的语句

4. external linkagestatic变量(static variables with external linkage

这类变量具有file scopeexternal linkagestatic storage duration。这种类别类别也被成为external storage class,属于这种类别的变量叫做external变量。将变量的声明放在任何函数外就可以创建一个external变量;同时通过使用关键词"extern"可以在函数中声明external变量(其实是引用别处已定义的external变量)。一个external变量只能被初始化一次;如果不显式初始化,external变量(或数组)会被自动初始化为0

如果某个external变量在一个源代码文件中被定义,而在其他的源代码文件中被使用,则这些其他文件必须在声明这个变量时使用"extern"。如果file scope的变量有关键词"extern",则被引用的原变量必须有external linkage;如果block scope的变量有关键词"extern",则被引用的原变量必须有internal linkage或external linkage

例:extern int x; int main(void) ...

compiler会认为变量"x"真正的定义在程序的其他位置,可能是另一个文件,所以并不会给x分配空间。因此,不要使用"extern"来创建external定义,只能用它来引用一个已经存在的external定义

5. internal linkagestatic变量(static variables with external linkage

这类变量具有file scopeinternal linkagestatic storage duration,可以通过将变量的声明放在任何函数外并添加关键词"static"来创建,只能被同一个transition unit中的函数使用。它也同样只能在compile时被初始化一次,如果不显示初始化则初始化为0



C语言有6个storage class分类符:auto, register, static, exter, _Thread_local和typedef。在大多数情况下在一个storage class的声明中不能使用一个以上的分类符,这意味着不能在typedef中使用storage class分类符。唯一的例外是_Thread_local可以和static或extern同时使用

static用法:

1. 当用于file scope变量时,表示将变量声明为internal linkage,只能用于一个translation unit

2. 当用于block scope变量时,表示将变量声明为static storage duration,变量将在程序运行过程中一直存在



函数可以是external(默认)也可以是static,如果想让一个函数只能在一个translation unit中使用,则可以在声明时使用关键词"static";若函数的定义在其他位置,则需要通过"extern"来声明(这些都和变量的用法一样)



malloc()

malloc()函数只有一个参数——程序员需要的内存byte数;malloc()分配了空间但并未给空间分配名字,但它会返回所获得空间第一个byte的地址,因此可以通过将这个地址赋给一个指针而使用它。malloc()的返回值类型是pointer-to-void的指针,这种指针是一个泛化指针,可以被赋给任何类型的指针。如果malloc()找不到要求的空间,会返回一个空指针

例:double* p; p = (double*)malloc(30 * sizeof(double)); free(p)

free()的参数应该是malloc()返回的那个指针,它只能释放malloc()分配的空间,不能释放通过其他方式获得的空间。在Linux下使用free()时发现,它的作用只是告知系统p所指向的空间可以使用,然后会将其置为0,但是,注意,p的值并没有被置为NULL,并且因为p仍然指向原来的地址,甚至可以使用p去给那个空间赋值



calloc()

作用与malloc相同,但会将所分配的空间中所有的bit设为0(但在有些硬件系统中,浮点数的0并不代表0)。它所分配的空间也需要通过free()来释放

例:double* p; p = (double*)calloc(30, sizeof(long));



动态内存分配与变长数组(VLA):

相同:都可以在程序运行时决定所创建数组的大小

不同:
1. VLAautomatic storage,当程序离开定义VLAblock时,内存就被释放了

2. malloc()所创建数组的访问不被局限在一个函数中,例如一个函数可以创建一个数组并返回指针,可以使调用它的函数使用这个数组(注意不要free两次)

3. VLA处理多维数组更简单




使用头文件的好处:

不必记忆在哪个文件中进行定义,在哪个文件中进行调用(要使用extern),所有的文件只要都包含同一个头文件即可

坏处:

主要是变量的重复,如:

/*1.h */ static const double PI = 3.14;

/*2.h */ #include "1.h"

在这种情况下,必须要使用关键词static让变量具有internal linkage,否则在每个文件中都会定义一个external"PI",是不合法的(前面提到过,#include"*.h"是把头文件的内容放在此处)



类限定词(type qualifier):

1. const

可以用来保护global variables

2. volatile

警告compiler一个变量的值可以被除程序之外的东西改变(如一个存储了当前时间的变量可以被系统改变),以限制compilercache等的优化(如果没有volatilecompiler会假定这个变量的值不会被程序之外的东西改变,进而会进行优化)。一个变量可以既是const又是volatile的,const指示表示不能被程序改变

3. restrict

主要是为了通过给compiler更多权限来进行优化,它只能用于指针,表示该指针是访问数据object初始且唯一的方式

4. _AtomicC11

要包含"stdatomic.h""threads.h",主要用于并行计算。当一个thread对一个atomic类型的object进行atomic的处理时,其他thread不能访问这个object



C99允许将类限定词和storage class限定词"static"放在function形参列表的第一个方括号中,如

void func(int* const a, int* restrict b, int c); == void func(int a[const], int b[restrict], int c);

static产生了新的含义,如【double stick(double ar[static 20]);】表示函数调用时的实际参数会是指向至少有20个元素的数列的第一个元素的指针。这样做的目的是能够是compiler使用这个信息进行代码优化


你可能感兴趣的:(C的存储类,链接与内存管理(Storage Class, Linkage, Memory Management))