结构
- 结构的基本知识
- 结构与函数
- 传递结构
- 结构数组、指向结构的指针
- 自引用结构(二叉树)
- 表查找
- 类型定义(typedef)
- 联合
- 位字段
结构也是一种数据类型。类似于int、char、double、float等。
结构是一个或多个变量的集合,这些变量可能为不同的类型,为了处理的方便而将这些变量组织在一个名字之下。
结构可以拷贝、赋值、传递给函数,函数也可以返回结构类型的返回值。
结构的基本知识
- 关键字struct 引入结构声明。结构声明由包含在花括号内的一系列声明组成。
- 关键字struct后面的名字是可选的,称为结构标记。结构标记用于为结构命名,在定义之后,结构标记就代表花括号内的声明,可以用它作为该声明的简写形式。
- 结构中定义的变量称为成员。
struct point {int x;int y;
};
- 结构中定义的变量称为成员。结构成员、结构标记和普通变量(即非成员)可以采用相同的名字,它们之间不会冲突,因为通过上下文分析总可以对它们进行区分。
- 如果结构声明的后面不带变量表,则不需要为它分配存储空间,它仅仅描述了一个结构的模板或轮廓。
- 结构的初始化可以在定义的后面使用初值表进行。
struct point maxpt = {320, 200};
- 可以通过下列形式引用某个特定结构中的成员:结构名.成员
printf("%d,%d", pt.x, pt.y);
- 结构可以嵌套。
struct rect {struct point pt1;struct point pt2;
};
结构与函数
结构的合法操作只有几种:作为一个整体复制和赋值,通过&运算符取地址,访问其成员。
传递结构
- 分别传递各个结构成员
- 传递整个结构(可采用函数返回值)
- 传递指向结构的指针
struct point *pp;
假定p 是一个指向结构的指针,可以用p->结构成员,这种形式引用相应的结构成员。
结构数组、指向结构的指针
下面两种类型相同:
struct key {char *word;int count;
};
struct key keytab[NKEYS];
struct key {char *word;int count;
} keytab[NKEYS];
自引用结构(二叉树)
用结构体来实现二叉树。
- 结构体的自引用:
struct tnode { /* the tree node: */char *word; /* points to the text */int count; /* number of occurrences */struct tnode *left; /* left child */struct tnode *right; /* right child */
};
- 两个结构相互引用
struct t {...struct s *p; /* p points to an s */
};
struct s {...struct t *q; /* q points to a t */
};
表查找
该算法采用的是散列查找方法——将输入的名字转换为一个小的非负整数,该整数随后将作为一个指针数组的下标。数组的每个元素指向某个链表的表头,链表中的各个块用于描述具有该散列值的名字。如果没有名字散列到该值,则数组元素的值为NULL。
链表中的每个块都是一个结构,它包含一个指向名字的指针、一个指向替换文本的指针以及一个指向该链表后继块的指针。如果指向链表后继块的指针为NULL,则表明链表结束。
struct nlist { /* table entry: */struct nlist *next; /* next entry in chain */char *name; /* defined name */char *defn; /* replacement text */
};
类型定义(typedef)
C语言提供了一个称为typedef的功能,它用来建立新的数据类型名.
typedef int Length; //将Length定义为与int具有同等意义的名字。
typedef char* String;
typedef 中声明的类型在变量名的位置出现,而不是紧接在关键字typedef 之后。这里以大写字母作为typedef定义的类型名的首字母,以示区别。
从任何意义上讲,typedef 声明并没有创建一个新类型,它只是为某个已存在的类型增加了一个新的名称而已。
typedef 声明也没有增加任何新的语义:通过这种方式声明的变量与通过普通声明方式声明的变量具有完全相同的属性。实际上,typedef类似于#define 语句,但由于typedef 是由编译器解释的,因此它的文本替换功能要超过预处理器的能力。
typedef int (*PFI)(char *, char *);
该语句定义了类型PFI 是“一个指向函数的指针,该函数具有两个char *类型的参数,返回值类型为int”,它可用于某些上下文中。
除了表达方式更简洁之外,使用typedef还有另外两个重要原因。首先,它可以使程序参数化以提高程序的可移植性。如果typedef声明的数据类型同机器有关,那么,当程序移植到其它机器上时,只需改变typedef类型定义就可以了。一个经常用到的情况是,对于各种不同大小的整型值来说,都使用通过typedef 定义的类型名,然后,分别为各个不同的宿主机选择一组合适的short、int 和long 类型大小即可。
typedef 的第二个作用是为程序提供更好的说明性——Treeptr 类型显然比一个声明为指向复杂结构的指针更容易让人理解。
联合
联合是可以(在不同时刻)保存不同类型和长度的对象的变量,编译器负责跟踪对象的长度和对齐要求。联合提供了一种方式,以在单块存储区中管理不同类型的数据,而不需要在程序中嵌入任何同机器有关的信息。
联合的目的:一个变量可以合法地保存多种数据类型中任何一种类型的对象。
union u_tag {int ival;float fval;char *sval;
} u;
变量u 必须足够大,以保存这3 种类型中最大的一种,具体长度同具体的实现有关。这些类型中的任何一种类型的对象都可赋值给u,且可使用在随后的表达式中,但必须保证是一致的:读取的类型必须是最近一次存入的类型。
- 访问联合中成员
联合名.成员、 联合指针->成员 - 联合使用
if (utype == INT)printf("%d\n", u.ival);
if (utype == FLOAT)printf("%f\n", u.fval);
if (utype == STRING)printf("%s\n", u.sval);
elseprintf("bad type %d in utype\n", utype);
- 结构体中联合使用:与嵌套结构相同
struct {char *name;int flags;int utype;union {int ival;float fval;char *sval;
} u;
} symtab[NSYM];symtab[i].u.ival;
实际上,联合就是一个结构,它的所有成员相对于基地址的偏移量都为0,此结构空间要大到足够容纳最“宽”的成员,并且,其对齐方式要适合于联合中所有类型的成员。
- 初始化
联合只能用其第一个成员类型的值进行初始化,因此,上述联合u 只能用整数值进行初始化。
位字段
在存储空间很宝贵的情况下,有可能需要将多个对象保存在一个机器字中。
#define KEYWORD 01
#define EXTRENAL 02
#define STATIC 04enum { KEYWORD = 01, EXTERNAL = 02, STATIC = 04 };
C语言仍然提供了另一种可替代的方法,即直接定义和访问一个字中的位字段的能力,而不需要通过按位逻辑运算符。位字段(bit-field),或简称字段,是“字”中相邻位的集合。“字”(word)是单个的存储单元,它同具体的实现有关。
struct {
unsigned int is_keyword : 1;
unsigned int is_extern : 1;
unsigned int is_static : 1;
} flags;
字段的所有属性几乎都同具体的实现有关。字段是否能覆盖字边界由具体的实现定义。字段可以不命名,无名字段(只有一个冒号和宽度)起填充作用。特殊宽度0 可以用来强制在下一个字边界上对齐。