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表查找
散列查找法: 将输入的名字转换为一个小的非负整数,该整数随后将作为一个指针数组的下标
数组的每个元素指向某个链表的表头,链表中的各个块用于描述具有该散列值的名字,如果没有名字散列到该值,则数组元素的值为NULLunsigned 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 *或字符指针同义,此后,便可以在类型声明和类型转换中使用Stringtypedef中声明的类型在变量名的位置出现,而不是紧接着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)或简称字段,是字中相邻位的集合某些机器上字段的分配是从字的左端至右端进行的,而某些机器相反。