6.1结构的基本知识
结构是一个或多个变量的集合,这些变量可能为不同的类型,为了处理的方便而将这些变量组织在一个名字之下 结构可以拷贝、赋值、传递给函数,函数也可以返回结构类型的返回值 自动结构和数组现在也可以进行初始化 struct point{ int x; int y; } 关键字struct引入结构声明 结构声明由包含在花括号内的一系列声明组成 关键字struct后面的名字是可选的,称为结构标记 结构标记用于为结构命名,在定义结构后,结构标记就代表花括号内的声明,可以用它作为声明的简写形式 结构中定义的变量称为成员 结构成员、结构标记、和普通变量可以采用相同的名字,不会冲突 struct声明定义了一种数据类型 在标记结构成员结束的右花括号之后可以跟一个变量表 struct{ ... } x,y,z; 这和 int x,y,z 具有类似的意义,这两个声明都将x、y与z声明为指定类型的变量,并分配存储空间 如果结构声明后面不带变量表,则不需要为它分配存储空间,它仅仅描述了一个结构的模板 如果结构声明中带有标记,那么在以后定义结构实列时便可以使用该标记定义 struct point pt; 在表达式中可以通过下列形式引用某个特定的结构中的成员 结构名.成员 结构可以嵌套
6.2结构与函数
结构的合法操作只有几种: 作为一个整体复制和赋值 通过&运算符去 地址,访问其成员 复制和赋值包括向函数传递参数以及从函数返回值 结构之间不可以进行比较 可以用一个常量成员值列表初始化结构 自动结构也可以通过赋值初始化 如果传递给是的结构很大,使用指针的效率通常比复制整个结构的效率高 结构指针类似于普通变量指针 声明 struct point *pp; 将定义一个指向struct point类型的指针 如果pp指向一个point结构,那么*pp即为该结构 而*(pp).x和*(pp).y则是结构成员 *(pp).x中圆括号是必须的,因为结构成员运算符"." 的优先级比"*"的优先级高 表达式 *pp.x含义等于 *(pp.x) ,x不是指针,所以是非法的 结构指针的使用频率非常高,为了方便,C语言提供另一种简写 p->结构成员 在所有的运算符中4个优先级最高: 结构运算符. 和-> 函数调用的() 已经下标[] 表达式 ++pt->len 将增加len的值,而不是增加pt的值,其中隐含的括号关系 ++(pt->len)
6.3结构数组
数组的长度在编译时已经完全确定,它等于数组项的长度乘以项数 C语言提供了一个编译时计算任一对象的长度的运算符 sizeof 表达式 sizeof 对象 或 sizeof(类型名) 将返回一个整形值,他等于指定对象或类型占用的空间字节数 严格来说sizeof返回的是无符号整形,其类型为size_t 条件编译语句#if中不能使用sizeof,因为预处理器不对类型名进行分析 但预处理器并不计算#define语句中的表达式,因此 #define中使用sizeof是合法的
6.4指向结构的指针
千万不要认为结构的长度等于各成员长度的和。因为不同的对象有不同的对齐要求。 所以结构中可能会出现未命名的"空穴"(hole) 例如: struct{ char c; int i; } 可能需要8字节的存储空间,而不是5字节。使用sizeof可以返回正确的对象长度
6.5自引用结构
struct tnode{ char *word; int count; struct tnode *left; struct tnode *right; } 一个包含其自身实列的结构是非法的 但是,下列声明是合法的 struct tnode *left; 它将left声明为指向tnode的指针,而不是tnode本身
6.6表查找
散列查找法: 将输入的名字转换为一个小的非负整数,该整数随后将作为一个指针数组的下标 数组的每个元素指向某个链表的表头,链表中的各个块用于描述具有该散列值的名字,如果没有名字散列到该值,则数组元素的值为NULL unsigned hash(char *s){ unsigned hashval; for(hashval=0; *s != '\0';s++) hashval = *s+31*hashval; return hashval % HASHSIZE; } 由于在散列计算时采用的是无符号算术运算,因此保证了散列值非负
6.7类型定义
C语言提供了一个typedef的功能,用来建立新的数据类型名 例如: typedef int Length; 将Length 定义为何int具有同等意义的名字。 类型Length可用于类型什么、类型转换等 类似于 typedef char *String 将String定义为和char *或字符指针同义,此后,便可以在类型声明和类型转换中使用String typedef中声明的类型在变量名的位置出现,而不是紧接着typedef之后 从任何意义上讲,typedef声明并没有创建一个新类型,它只是为某个已存在的类型增加了一个新的名字 实际上typedef类似于#define语句,但由于typedef是编译器解释的,因此他的文本替换功能要超过预处理的能力 除了表达式更简洁外,typedef使用还有两个重要原因 首先它可以是程序参数化,以提高程序的可移植性 第二是一位程序提供更好的说明性---Treeptr类型显然比一个声明为指向复杂结构的指针更容易让人理解
6.8联合
联合是可以在不同时刻保存不同类型和长度的对象的变量,编译器负责跟踪对象的长度和对齐要求。 联合提供了一种方式,在单块存储区中管理不同类型的数据,而不需要程序中嵌入任何机器有关的信息 例如: union u_tag{ int ival; float fval; char *sval; } u; 变量u必须足够大,以保存3种类型中最大的一种,具体长度同具体的实现有关 这些类型中的任何一种类型的对象都可以赋值给u,且可使用在随后的表达式中,但必须保证是一致的: 读取的类型必须是最近一次存入的类型。 程序员负责跟踪当前保存在联合中的类型 如果保存的类型和读取的类型不一致,其结果取决于具体的实现 联合可以使用在结构和数组中,反之亦可 实际上,联合就是一个结构,他的所有成员相对于基地址偏移量都为0 此结构空间要大到足够容纳最宽的成员。 并且,其对齐方式要适合联合中所有的类型的成员。 对联合允许的操作与对结构允许的操作相同: 作为一个整体单元进行复制 赋值 取地址及访问成员 联合只能用第一个成员类型的值进行初始化,因此,联合u只能用整数进行初始化
6.9 位字段
在存储空间很宝贵的情况下,有可能需要将多个对象保存在一个机器字中。一种常用的方法是,使用类似于编译器符号表的单个二进制位标志集合。 外部强加的数据格式也经常需要从字的部分位中读取数据 考虑编译器中符号表操作的有关细节 程序中的每个标识符都有与之相关的特定信息 C语言提供了一种直接定义和访问一个字中的位字段的能力,而不需要通过按位操作 位字段(bit-field)或简称字段,是字中相邻位的集合 某些机器上字段的分配是从字的左端至右端进行的,而某些机器相反。