1、结构
1.1 结构声明
在声明结构时,必须列出它包含的所有成员.这个列表包括每个成员的类型和名称.
struct tag {member-list} variable-list;
例如
//A
struct {
int a;
char b;
float c;
} x;
//B
struct {
int a;
char b;
float c;
} y[20], *z;
注意:这两个声明会被编译器当作两种截然不同的类型,即使他们的成员列表完全相同.
下面这跳语句是非法的
z = &x;
可以利用tag(标签)字段来给结构的成员列表提供一个名字. 标签允许多个声明使用同一个成员列表,并创建同一种类型的结构.
//标签 SIMPLE 和这个成员列表联系在一起了. 但它没有创建任何变量. 标签不是结构的名字,更不能用来定义结构
struct SIMPLE {
int a;
char b;
float c;
};
//根据标签声明结构
struct SIMPLE x;
struct SIMPLE y[20], *z;
z = &x;//x ,y ,z 都是同一种类型的结构变量
一种更好的定义结的方法:
typedef struct {
int a;
char b;
float c;
} Simple;
Simple x;
Simple y[20], *z;
现在 Simple 是结构的名称了,而不是成员列表的标签.
1.2 结构成员
结构成员可以是标量,数组,指针甚至是其他结构
struct COMPLEX {
float f;
int a[10];
long *lp;
struct SIMPLE s;
struct SIMPLE sa[10];
struct SIMPLE *sp;
};
一个结构的成员的名字可以和其他结构的成员的名字相同,所以这个结构的成员a 并不会与 struct SIMPLE s; 的成员a冲突.
1.3 结构体成员的访问
直接访问
如果有一个结构体
struct COMPLEX comp;
可以用 . 访问结构调整的成员
comp.s
comp.s.a 或者 (comp.s).a
( (comp.sa)[4] ).c 或者 comp.sa[4].c
点操作符的结合是从左到右的
间接访问
如果拥有一个指向结构的指针,这时要访问这个结构的成员.要首先对指针执行间接反问操作.
然后使用点操作符反问它的成员. 但是点操作符的优先级是高于间接访问操作符的.所以你必须在表达式中使用括号..
void func( struct COMPLEX *cp);
(*cp).f;
由于**点操作符的优先级是高于间接访问操作符 ** 使得间接访问必须加括号,这一点体验很不好.
好在C语言提供了更为方便的箭头操作符来完成间接访问 ->
例如上例
cp->f
cp->a
cp->s
但是使用箭头操作符访问成员时,左操作数必须是一个指向结构的指针
1.4 结构的自引用
//非法
struct SELF_REF1 {
int a;
struct SELF_REF1 b;
int c;
};
//合法
struct SELF_REF2 {
int a;
struct SELF_REF2 *b;
int c;
};
结构自引用的形式必须是指针.编译器在结构的擦含高浓度确定之前就已经知道指针的长度,所以这种类型的自引用是合法的.
不用纠结,因为它事实上所指向的是同一种类型的不同结构. 链表和树都是用这种技巧实现的.
一个陷阱:
typedef struct {
int a;
SELF_REF3 *b;
int c;
} SELF_REF3;
这个声明的目的是为这个结构创建类型名 SELF_REF3, 但是这样会失败.因为类型名直到声明的末尾才定义,所以在结构声明的内部它尚未定义.
解决方案是定义一个结构标签来声明b,如下
typedef struct SELF_REF3_TAG {
int a;
struct SELF_REF3_TAG *b;
int c;
} SELF_REF3;
1.5 不完整的声明
如果必须声明一些相互之间存在依赖的结构,并且每个结构都引用来其他结构的标签,那么该首先声明那些结构呢?
这个时候就用到不完整声明了.
struct B;
struct A {
struct B *partner;
//...
};
struct B {
struct A *partner;
//...
};
1.6 结构的初始化
值放在花括号内部,由都好分割初始值列表.
struct INT_EX {
int a;
short b[10];
Simple c;
} x = {
10,
{1, 2, 3, 4, 5 },
{25, 'x', 1.9}
};
2 结构、指针和成员
直接通过指针访问结构和它们的成员的操作符是相当简单的.