第八章 数组
8.1 一维数组
8.1.1 数组名
数组具有一些和指针完全不同的特征,数组具有确定数量的元素,而指针只是一个标量值。编译器用数组名来记住这些属性,只有当数组名在表达式中使用时,编译器才为他产生一个指针常量。只有在两种场合下,数组名并不用指针常量来表示:就是当数组名作为sizeof
操作符或取地址操作符&
的操作数时。前者返回整个数组的长度,而不是指向数组的指针的长度。取一个数组名的地址所产生的是一个指向数组的指针,而不是要给指向某个常量值的指针。表达式&a[0]
是一个指向数组第0个元素的指针,但那正是数组名本身的值,所以int int a[10]; int b[10]; int *c; *c = a;
与 int *c = &a[0];
这两语句执行的任务是完全一样的。如果数组名表示整个数组,这条语句表示整个数组被复制到一个新的数组,但事实不是这样,实际被赋值的是一个指针的拷贝,c所指向的是数组的第0个元素。因此b = a;
这种赋值是非法的。不能把一个数组的所有元素通过赋值符复制到另一个数组,必须使用一个循环,每次复制一个元素。a=c
也不行因为a
的值是个常量,不能被修改。
8.1.2 下标引用
语句*(b+3)
中b
的值是指向整型的指针,加法运算的结果是另一个指向整型的指针,它所指向的是数组第0个元素向后移3个整数长度的位置。然后,间接访问操作符*
访问这个新位置,或者取得那里的值,或者把一个新值存储与该处。这个过程听上去和下标引用的执行过程完全相同,除了优先级外,下标引用和间接访问完全相同,也就是array[i]
和*(array+i)
完全相同。
8.1.3 指针与数组
指针和数组并不是相等的,有如下两个声明:int a[5]; int *b;
都具有指针值,它们都可以进行间接访问和下标引用操作,但是它们还是存在相当大的区别。声明数组时,编译器根据指定的数量为数组保留内存空间,然后再创建数组名,它的值是一个指针常量,指向这段空间的起始位置。声明一个指针变量时,编译器只为指针本身保留内存空间,它并不为任何整型值分配内存空间。而且指针变量并未初始化为指向任何现有的内存空间,如果它是一个自动变量,它甚至根本不会初始化。
8.1.4 作为函数参数的数组名
8.1.5 初始化
数组初始化取决于它们的存储类型,存储与静态内存的数组只初始化一次,是由链接器完成的,如果数组未被初始化,将会自动初始化为零。但是对于自动变量而言,由于自动变量位于运行时堆栈中,执行流每次进入它们所在的代码块时,这类变量每次所处的内存位置可能并不相同。在程序开始之前,编译器没有办法对这些位置进行初始化,所以自动变量在缺省情况下是未初始化的。如果自动变量的声明中给出了初始值,每次当执行流进入自动变量声明所在的作用域时,变量就被一条隐式的赋值语句初始化,这条语句和普通赋值语句一样需要时间和空间来执行,数组的问题在于初始化列表中可能有很多值,这就可能产生许多条赋值语句。因此这里需要权衡利弊,当数组初始化局部与一个代码块或函数时,需要考虑在程序的执行流每次进入该函数或代码块时,每次都对数组进行重新初始化是不是指的,如果是否定的,就要把数组声明为static
,这样数组的初始化只需在程序开始前执行一次。
第九章 字符串、字符和字节
9.1 字符串基础
字符串就是一串零个或多个字符,并且以一个位模式位全0的NUL字节结尾。字符串的长度就是它所包含的字符个数
9.2 不受限制的字符串函数
第十章 结构和联合
10.1 结构基础知识
聚合数据类型能够同时存储超过一个的单独数据,C语言提供了两种类型的聚合数据类型,数组和结构。数组是同类型元素的集合,结构是一些值的集合。数组元素可以通过下标访问,但是结构是通过名字访问的。和数组名不同,当一个结构变量在表达式中使用时,它并不被替换成一个指针,结构变量也无法使用下标来选择特定的成员。结构变量属于标量类型。
10.1.1 结构声明
在声明结构时,必须列出它所包含的所有成员。
10.3 结构的存储分配
第十一章 动态内存分配
第十三章 高级指针话题
第十四章 预处理器