C中,object指一块内存区域,一个object可以存储一或多个值。一个object可能还未存储任何值,但是它有存储一个恰当值的合适大小。"int entity =3;"声明了一个叫做entity的identifier。identifier是一个名字,它指定一个具体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 linkage:external linkage的变量可用在一个多文件的程序中的任意位置
2. internal linkage:internal linkage的变量可以用在一个translation unit中的任何位置
3. no linkage:block scope, function scope和function prototype scope的变量是no linkage,意味着它们只是他们被定义的block, function或function prototype的私有变量
file scope的变量可以是internal linkage或external linkage。通常"file scope with internal linkage"变量被称为"file scope"变量,而"file scope with external linkage"的变量被称为"global scope"或"program scope"。file scope变量默认是external linkage,要想将其设为internal linkage需要在声明变量时加上"static"
scope和linkage描述了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 scope,no linkage和static storage duration。VLA有一点不同是他们从被声明时才开始存在
4. allocated storage duration
由程序员决定变量的duration(malloc()- free())
Storage Class:
1. automatic
属于automatic storage class的变量有automatic storage duration,block scope和no linkage。任何在block或function中定义的变量默认都是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 scope,no linkage和automatic storage duration。要声明一个register变量需要关键词"register",但即便使用了关键词也不一定能获得一个register变量,compiler会决定是否给予一个register变量,如果compiler忽略了这个请求,将只能获得一个普通automatic变量,但依然无法使用地址操作符来处理它
3. 有block scope的static变量(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 linkage的static变量(static variables with external linkage)
这类变量具有file scope,external linkage和static 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 linkage的static变量(static variables with external linkage)
这类变量具有file scope,internal linkage和static 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. VLA是automatic storage,当程序离开定义VLA的block时,内存就被释放了
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一个变量的值可以被除程序之外的东西改变(如一个存储了当前时间的变量可以被系统改变),以限制compiler对cache等的优化(如果没有volatile,compiler会假定这个变量的值不会被程序之外的东西改变,进而会进行优化)。一个变量可以既是const又是volatile的,const指示表示不能被程序改变
3. restrict
主要是为了通过给compiler更多权限来进行优化,它只能用于指针,表示该指针是访问数据object初始且唯一的方式
4. _Atomic(C11)
要包含"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使用这个信息进行代码优化