1、 结构的基本知识
struct point{
int x;
int y;
};
关键字struct引入结构声明。结构声明由包含在花括号内的一系列声明组成。struct后面的名字是可选的,称为结构标记。结构标记用于为结构命名,在定义之后,结构标记就代表花括号内的声明。
结构中定义的变量称为成员。结构成员、结构标记和普通变量(即非成员)可以采用相同的名字,它们之间不会冲突,但通常只有密切相关的对象才会使用相同的名字。
struct声明定义了一种数据类型。在标志结构成员表结束的右花括号之后可以跟一个变量表,这与其他基本类型的变量声明是相同的。如:
struct { ... }x,y,z; 这种声明方式其实类似 int x,y,z; 这两个声明都将x、y、z声明为制定类型的变量,并且为它们分配存储空间。
如果结构声明的后面不带变量表,则不需要为它分配存储空间,它仅描述了一个结构的模板或轮廓。但是如果结构声明中带有标记,那么在以后定义结构实例时可以使用该标记定义。如对上面给出的结构声明point,语句struct point pt;定义了一个struct point型变量pt。
结构可以嵌套。例如:
struct rect{
struct point pt1;
struct point pt2;
};
结构rect包含两个point类型的成员。
2、 结构与函数
结构的合法操作只有几种:作为一个整体复制和赋值,通过&运算符取地址,访问其成员。其中,复制和赋值包括向函数传递参数已经从函数返回值。结构之间不可以进行比较。可以用一个常量成员值列表初始化结构。
在所有运算符中,下面4个运算符的优先级最高:结构运算符”.”和”->”、对于函数的调用的”()”和用于下标的”[]”,但要注意这些符号的结合方向是自右向左。
3、 结构数组
条件编译语句#if中不能使用sizeof,因为预处理器不对类型名进行分析。但预处理器并不计算#define语句中的表达式,因此,在#define中使用sizeof是合法的。
4、 指向结构的指针
再次提醒:指针的加法运算是非法的,但是指针的减法运算是合法的。
千万不要认为结构的长度等于各成员长度的和。因为不同的对象有不同的对齐要求,所以,结构中可能会出现未命名的“空穴(hole)”。如,假设char占用一个字节,int占用4个字节,则下列结构:
struct{
char c;
int i;
};可能需要8个字节而不是5个。使用sizeof运算符可以返回正确的对象长度。
建议:当函数返回值类型比较复杂时(如结构指针),如,
struct key *binsearch(char *word,struct key *tab,int n)
很难看出函数名,也不太容易使用文本编辑器找到函数名。所以可以这样写:
struct key *
binsearch(char *word,struct key *tab,int n)
5、 自引用结构
自引用结构的例子如下:
struct tnode{
char *word;
int count;
struct tnode *left;
struct tnode *right;
};
这种对节点的递归的声明方式看上去好像似乎不确定的,但它的确实正确的。一个包含其自身实例的结构是非法的,但是struct tnode *left;是将left声明为指向tnode的指针,而不是tnode实例本身。
6、 表查找
7、 类型定义(typedef)
C语言提供了一个称为typedef的功能,它用来建立新的数据类型名,如,声明
typedef int Length;
则Length为与int具有同等意义的名字。类型Length可用于声明、类型转换等,和int完全相同。如:Length len,maxlen; Length *lengths[];
注意,typedef中声明的类型在变量名的位置出现,而不是紧接在关键字typedef之后。typedef在语法上类似于存储量extern、static等。
下面看更复杂的例子:
typedef struct tnode *Treeptr;
typedef struct tnode{
char *word;
int count;
struct tnode *left;
struct tnode *right;
}Treenode
上述类型定义创建了两个新类型关键字:Treenode(一个结构)和Treeptr(一个指向该结构的指针)。
这里必须强调的是,从任何意义上讲,typedef声明并没有创建一个新类型,它只是为某个已存在的类型增加了一个新的名称而已。typedef声明也没有增加任何新的语义:通过这种方式声明的变量与通过普通声明方式声明的变量具有完全相同的属性。实际上,typedef类似于#define语句,但由于typedef是由编译器解释的,因此它的文本替换功能要超过预处理器的能力。如:
typedef int (*PFI)(char *,char *);
该语句定义了类型PFI是“一个指向函数的指针,该函数具有两个char *类型的参数,返回值类型为int”。如 PFI strcmp,numcmp;
除了表达方式更简洁之外,使用typedef还有两个重要原因。首先,它可以使程序参数化,以提高程序的可移植性。如果typedef声明的数据类型同机器有关,那么当程序移植到其他机器上时,只需改变typedef类型定义就可以了。第二,为程序提供更好的说明性——Treeptr类型显然比一个声明为指向复杂结构的指针更容易让人理解。
8、 联合
联合是可以(在不同时刻)保存不同类型和长度的对象的变量,编译器负责跟踪对象的长度和对齐要求。实现的是,一个变量可以合法地保存多种数据类型中任何一种类型的对象。其语法结构如下:
union u_tag{
int ival;
float fval;
char *sval;
}u;
变量u必须足够大,以保存这3种类型中最大的一种。
实际上,联合就是一个结构,它的所有成员相对于基址的偏移量都为0,此结构空间要大到足够容纳最“宽”成员,且对齐方式要适合于联合中所有类型的成员。对联合允许的操作与对结构允许的操作相同:作为一个整体单元进行赋值、复制、取地址及访问其中一个成员。
注意:联合只能用其第一个成员类型的值进行初始化。