10 结构和联合

10.1 结构基础知识

聚合数据类型(aggregate data type)能同时存储多个数据。c中有两种聚合数据类型:数组和结构。

  • 数组:相同元素的集合。通过下标访问

  • 结构:不同元素的集合。通过成员名访问

两种常用的声明方式:

// 这种方式把tag_name和成员列表绑定在一起,但并未创建任何变量
struct tag_name {
int a;
char b;
float c;
};
// 这种写法实际上是把name定义为这个结构的类型名
typedef struct {
int a;
char b;
float c;
} name;

结构的自引用

结构中不能直接包含自身,只能包含指向自身的指针。

// 错误的写法
struct Node {
int a;
struct Node b;
}
// 正确的写法,因为指针的长度是已知且固定的。
struct Node {
int a;
struct Node *b;
}

10.2 结构的存储分配

我们以一个例子来说明结构的边界对齐。align占12字节,align2占8字节。因为a后面有3个空着的字节。c后面也有3个空着的字节。在align2中,b占四个字节,ac占两个字节,后面空两个字节。

offsetof宏返回成员开始存储的位置距结构起始位置偏移的字节数目。

#include<stdio.h>
#include<stddef.h> // offsetof 定义的文件
struct ALIGN
{
char a;
int b;
char c;
};
struct ALIGN2
{
int b;
char a;
char c;
};
int
main ()
{
struct ALIGN align;
struct ALIGN2 align2;
printf("sizeof(align) = %lu\n", sizeof(align));
printf("sizeof(align2) = %lu\n", sizeof(align2));
printf("offset of b in ALIGN = %lu\n", offsetof(struct ALIGN, b)); // 输出4
return 0;
}

10.3 结构作为函数参数

一般不直接把结构作为函数的参数,因为可能占用很大的空间,而是用指向结构的指针作为参数。

  • 优点:指针比结构小,把它压到栈上效率更高。

  • 缺点:函数内部可以通过指针修结构。如果不想让结构被修改,可以用const

typedef struct {
char product[PRODUCT_SIZE];
int quantity;
float unit_price;
float total_amount;
} Transaction;
void print_trans (Transaction const *trans);

10.4 位段

位段可以以精确到位的方式的操作内存。(位段的长度是整型的位长度的倍数)注意位段的可移植性会受到系统的影响。一般来讲,用位段的程序是不可移植的。

  • int被当做有符号还是无符号数。

  • int是16位还是32位。

  • 位段中的成员在内存中是从左向右分配还是从右向左分配的。

一个位段的例子。这个类型可以处理128(2\^7)个不同字符。64(2\^6)种不同的字体。以及0-524287个单位的长度。如果在16位的机器上声明这个位段,会报错。因为size占的位超过了16。

struct CHAR {
unsigned ch : 7;
unsigned font : 6;
unsigned size : 19;
};
struct CHAR ch1;

10.5 联合

直接来看一个联合使用的例子

// part是零件,从其他厂家获得
struct PARTINFO {
int cost;
int supplier;
};
// subassembly由多个零件和其他配件组成
struct SUBASSYINFO {
int n_parts;
struct {
char partno[10];
short quan;
} parts[MAXPARTS];
};
// inventory 存货,用来存放零件或subassy的信息。
struct INVREC {
char partno[10];
int quan;
enum { PART, SUBASSY } type;
union {
struct PARTINFO part;
struct SUBASSYINFO subassy;
} info;
};

注意联合以最大类型的成员作为其大小。如果成员的大小不同,可能浪费空间。更好的做法是在联合中存放指向成员的指针。指针的大小都是相同的。