结构

结构概念

结构是一个或多个变量的集合,这些变量可为不同的类型。

结构声明

struct 结构标记 { // 结构标记是可选的。
    各结构成员; // 各结构成员可为不同的类型。
} 变量名列表; //变量名列表是可选的,但结尾的;不可省略。
1
2
3
// 定义结构point,包含x和y两个成员;
struct point {
    int x;
    int y;
} p1, p2, p3; // 定义了三个结构变量。
1
2
3
4
5

结构定义

struct 结构标记 变量名;

// 定义point结构变量p4。
struct point p4;
1
2

struct 结构标记 变量名 = {初值表};

// 定义point结构变量p5,且初始化。
struct point p5 = {320, 320}; // 依次对应每个结构成员,必须为常量表达式。
1
2

通过.访问结构成员,如p5.xp5.y

结构嵌套

struct rect {
    struct point p1;
    struct point p1;
};
1
2
3
4

结构与函数

调用函数时传递结构的方式有:

  1. 分别传递各个结构成员。

  2. 传递整个结构。(复制一份副本传递)

  3. 传递指向结构的指针。(传递原结构的内存地址)

函数返回值为结构的方式有:

  1. 返回整个结构。(复制一份副本返回)

  2. 返回指向结构的指针。(返回原结构的内存地址)

结构数组

key结构声明,并定义一个key结构数组,长度为KEY_NUM。

struct key {
    char *word;
    int count;
};
struct key keys[KEY_NUM];
1
2
3
4
5

可像普通结构一样,在结构声明时定义一个结构数组变量。

struct key {
    char *word;
    int count;
} keys[KEY_NUM];
1
2
3
4

通过双层花括号,在结构数组定义时将其初始化。

struct key keys[] = {
    {"daking", 0},
    {"vichy", 0}
};
1
2
3
4

sizeof

获取结构对象的大小:sizeof(struct 结构标记);sizeof(结构对象);

size_t size_struct_key = sizeof(struct key);

struct key obj_key;
size_t size_obj_key = sizeof(obj_key);
1
2
3
4

获取结构数组对象的大小:sizeof(结构数组对象)

size_t size_keys = sizeof(keys);
1

获取结构数组的项数:sizeof(结构数组对象) / sizeof(结构对象)sizeof(结构数组对象) / sizeof(struct 结构标记)

size_t key_num = sizeof(keys) / sizeof(struct key);
size_t key_num = sizeof(keys) / sizeof(keys[0]);
1
2

可在#define中使用sizeof来确定结构数组的项数。

#define KEY_NUM (sizeof(keys) / sizeof(struct key))
1

千万不要认为结构的大小等于各成员长度的和,这是因为不同的对象有不同的对齐要求。使用sizeof运算符可以返回正确的对象大小。

// char占用1字节,int占用4字节,但最后结构可能占用8字节,而不是5字节。
struct s {
    char c;
    int i;
};
1
2
3
4
5

指向结构的指针

struct point *p;
1

结构指针访问结构成员有两种方式:.->

int x = (*p).x;
int x = p->x;
1
2

类型定义(typedef)

可通过typedef来建立新的数据类型别名。

typedef int Length;

Length len; // int类型的变量
1
2
3

注意,typedef声明并没有创新一个新类型,它只是为某个已存在的类型增加一个新的名称。

可利用typedef来为程序提供更好的说明性,Treenode类型比struct tnode更容易书写和理解。

typedef struct tnode {
    char *data;
    tnode *left;
    tnode *right;
} Treenode;

Treenode node; // tnode结构类型的变量
1
2
3
4
5
6
7

可利用typedef来声明与机器有关的数据类型,当程序移植到其他机器上时,只需改变typedef类型定义即可。如标准库中的size_t和ptrdiff_t等。

联合

联合是可以在不同时刻保存不同类型和大小的对象的变量。

联合的声明

联合变量的声明与结构声明类似。

union 联合标记 {
    各成员;
} 变量列表;
1
2
3
// 声明一个可保存int、float和char指针的联合变量u。
union u_tag {
    int ival;
    float fval;
    char *sval;
} u;
1
2
3
4
5
6

联合的成员

联合变量u必须足够大,以保存3种类型中最大的一种。

这些类型中的任何一种类型的对象都可赋值给u,而u的读取类型必须是最近一次存入的类型。

联合只能用其第一个成员类型的值进行初始化。因此,上述联合变量u只能用int值进行初始化。

联合的访问

访问联合中的成员的方式有:

  • 联合名.成员名;

  • 联合指针->成员名;

u.ival;
1

联合可使用在结构和数组中。

struct {
    char *name;
    int flags;
    int utype;
    union u_tag {
        int ival;
        float fval;
        char *sval;
    } u;
} symtab[NSYM];
1
2
3
4
5
6
7
8
9
10
symtab[i].u.ival;
1