10.2 结构体
结构体是一种由一系列成员组成的类型,这些成员按顺序分布在内存中。
1. 定义
关键字 struct + “名字” + 声明列表 其中声明列表是一个用逗号分隔,花括号环绕的列表,列表中的每一项是一个成员的声明
c
struct aircraft_carrier {
char* name;
int id;
};1
2
3
4
2
3
4
上述代码引入了一个新的类型 struct aircraft_carrier,并指定了它的含义。 这个类型包含两个成员:name 和 id。
typedef 起别名
c
typedef struct {
char* name;
int id;
} aircraft_carrier;1
2
3
4
2
3
4
或者
c
struct aircraft_carrier {
char* name;
int id;
};
typedef struct aircraft_carrier aircraft_carrier;1
2
3
4
5
2
3
4
5
取别名之后,可以用 aircraft_carrier 表示这个结构体类型。(在第二种情况中,struct aircraft_carrier 也可用)
2. 初始化
c
struct aircraft_carrier carrier = { "LiaoNing", 16 };
struct aircraft_carrier carrier2 = { .id = 17, .name = "ShanDong" };1
2
2
初始化结构体要用花括号环绕,逗号分隔的列表,列表中的每一项都应该是能初始化对应成员的表达式。 可以为列表中的表达式提供指派符(一个点 . 后加成员名称),初始化结构体内特定的成员。 第二行中 .id .name 即为指派符。
嵌套初始化
c
#include <stdio.h>
typedef struct {
int a;
int b[2];
} A;
typedef struct {
int c;
A d;
} B;
int main(void) {
A a1 = {.a = 1, .b[0] = 4, .b[1] = 5};
B arr1[] = {
{10, .d = a1, .d.a = 2}, // 此时 arr1[0]={10, {1, {4, 5} } };
[0].d.b[0] = 6 // 此时 arr1[0]={10, {1, {6, 5} } };
};
// B arr2[] = {{10, .d = a1, .d.a = 2}, [0] = {.d.b[0] = 6}};
printf("%d", arr1[0].d.a);
// printf("%d", arr2[0].d.a);
return 0;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
输出:
bash
2
注释掉的行会报 warning,因为在列表的第一个表达式里面已经初始化了 arr2[0],再初始化一遍会覆盖之前的初始化结果。
习题
#11001
⚡1⏳2
实现复数结构体,并实现加、减、乘、除运算,以及求模长和辐角。
#11002
⚡1⏳2
实现分数结构体,并实现其加减乘除运算。要求输出的结果为最简分数。
#11003
⚡4⏳6∑选做
实现初等函数结构体,并实现其求导和近似定积分的操作。
#11004
⚡2⏳3
实现 RGB 与 HSV 颜色模型之间的转换。
#11005
⚡5⏳4
若干传感器会分批上报统计记录,同一传感器可能出现多次。实现汇总函数:
c
#include <stddef.h>
#include <stdint.h>
typedef struct {
uint32_t sensor_id;
uint64_t sample_count;
uint64_t error_count;
uint64_t total_latency_us;
} SensorBatch;
typedef struct {
uint32_t sensor_id;
uint64_t sample_count;
uint64_t error_count;
uint64_t total_latency_us;
double error_rate;
double mean_latency_us;
} SensorSummary;
typedef enum {
SENSOR_SUMMARY_OK,
SENSOR_SUMMARY_INVALID,
SENSOR_SUMMARY_CAPACITY,
SENSOR_SUMMARY_OVERFLOW,
SENSOR_SUMMARY_OUT_OF_MEMORY
} SensorSummaryResult;
SensorSummaryResult summarize_sensor_batches(
const SensorBatch batches[],
size_t batch_count,
SensorSummary summaries[],
size_t summary_capacity,
size_t *out_count
);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
要求:
- 同一
sensor_id的计数和延迟总和应合并,结果按sensor_id严格递增排列。 - 每条记录都应满足
error_count <= sample_count;当sample_count == 0时,total_latency_us也必须为 0。 error_rate等于错误数除以样本数,mean_latency_us等于总延迟除以样本数;样本数为 0 时两者均为0.0。- 任意无符号加法溢出时返回
SENSOR_SUMMARY_OVERFLOW。 - 输出容量不足时返回
SENSOR_SUMMARY_CAPACITY。 - 任意失败都不得修改
summaries的内容和*out_count。 batch_count == 0时允许batches == NULL;summary_capacity == 0时允许summaries == NULL。- 输入数组、输出数组和
out_count指向的对象不得重叠。
成员访问运算符
. 用于结构体或联合体对象本身,-> 用于指向结构体或联合体对象的指针。a->b 与 (*a).b 语义等价,但后者更容易出错(括号不能省)。
c
struct point {
int x;
int y;
};
struct point p1 = {1, 2};
struct point *pp = &p1;
pp->x = 3;
p1.y = 4;1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
当一个接口以“对象指针”作为输入时,统一使用 -> 能减少视觉噪声。