第 10 章 结构和联合
# 第 10 章 结构和联合
# 结构基础知识
书本内容
# 结构声明
书本内容
# 结构成员
书本内容
# 结构成员的直接访问
书本内容
# 结构成员的间接访问
书本内容
# 结构的自引用
书本内容
# 不完整声明
书本内容
# 结构的初始化
书本内容
# 结构, 指针和成员
书本内容
# 访问指针
书本内容
# 访问结构
书本内容
# 访问结构成员
书本内容
# 访问嵌套的结构
书本内容
# 访问指针成员
书本内容
# 结构的存储分配
书本内容
# 作为函数参数的结构
书本内容
# 位段
书本内容
# 联合
书本内容
# 变体记录
书本内容
# 联合的初始化
书本内容
# 总结
书本内容
# 问题
#
问题 1
答案
- 结构成员
- 可以是不同的类型
- 通过名称进行访问
- 由于数据对齐,各个成员间可能存在未使用的空间
- 数组元素
- 都是同一类型
- 通过下标进行访问
- 各个元素之间没有因为数据对齐而导致的未使用空间
#
问题 2
答案
- 结构名
- 结构是一个标量
- 结构名作为右值使用时,表示的储存在结构中的值
- 结构名作为左值使用时,表示结构储存的内存位置
- 数组名
- 作为右值使用时,是指向数组第一个元素的指针
- 由于数组名相当于常量指针,因为其不能做为左值使用
#
问题 3
答案
完整的结构声明
struct S { int a; float b; } x;
1
2
3
4省略标签的声明
struct { int a; float b; } x;
1
2
3
4只定义标签,以便后续使用
struct S { int a; float b; }; struct S y;
1
2
3
4
5
6只声明标签
struct S;
1- 告知编译器 S 是一个结构体标签
使用
typedef
typedef struct { int a; float b; } Simple; Simple y;
1
2
3
4
5
6
#
问题 4
答案
abc
是一个结构标签,不是变量名,因此赋值语句是非法的
#
问题 5
答案
abc
是一个类型名,不是变量名,因此赋值语句是非法的
#
问题 6
答案
- 因为
x
存储在静态内存中,因此可以省略x.c = 0
struct
{
int a;
char b[10];
float c;
} x = {3, "hello"};
1
2
3
4
5
6
2
3
4
5
6
#
问题 7
答案
表达式 | 值 | 表达式 | 值 |
---|---|---|---|
nodes | 200 | &nodes[3].c->a | 200 |
nodes.a | illegal | &nodes->a | 200 |
nodes[3].a | 12 | np | 224 |
nodes[3].c | 200 | np->a | 22 |
nodes[3].c->a | 5 | np->c->c->a | 15 |
*nodes | nodes[0] | npp | 216 |
*nodes.a | illegal | npp->a | illegal |
(*nodes).a | 5 | *npp | 248 |
nodes->a | 5 | **npp | nodes[4] |
nodes[3].b->b | 248 | *npp->a | illegal |
*nodes[3].b->b | nodes[4] | (*npp)->a | 18 |
&nodes | 200 | &np | don't know |
&nodes[3].a | 236 | &np->a | 224 |
&nodes[3].c | 244 | &np->c->c->a | 212 |
#
问题 8
答案
- 16 位机器上浪费 2 个字节
- 16 位机器上
int
是 2 字节,需要 2 字节对齐 - 两个
char
类型各需补 1 位字节
- 16 位机器上
- 32 位机器上浪费 6 个字节
- 32 位机器上
int
是 4 字节,需要 4 字节对齐 - 两个
char
类型各需补 3 位字节
- 32 位机器上
#
问题 9
答案
int
位段即可能被当做有符号数,也可能被当做无符号数- 位段中位的最大数目与编译器和机器有关
- 许多编译器把位段成员的长度限制在一个整型值的长度以内
- 位段中的成员在内存中可能是从左向右分配,也可能是从右向左分配
#
问题 10
答案
// The complier allocates bit fields from left to right
struct FLOAT_FORMAT
{
unsigned int sign : 1;
unsigned int exponent : 7;
unsigned int fraction : 24;
};
// The complier allocates bit fields from right to left
// gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
struct FLOAT_FORMAT
{
unsigned int fraction : 24;
unsigned int exponent : 7;
unsigned int sign : 1;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
位段是从右向左排列的测试程序如下
#include <stdio.h> struct FLOAT_FORMAT { unsigned int fraction : 24; unsigned int exponent : 7; unsigned int sign : 1; }; int main(void) { float a = 12345.0; // 0x4640E400 float c = -12345.0; struct FLOAT_FORMAT *b; struct FLOAT_FORMAT *d; b = (struct FLOAT_FORMAT *)&a; d = (struct FLOAT_FORMAT *)&c; printf("%x\n", b->sign); printf("%x\n", b->exponent); printf("%x\n", b->fraction); printf("---------------\n"); printf("%x\n", d->sign); printf("%x\n", d->exponent); printf("%x\n", d->fraction); }
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输出
0 46 40e400 --------------- 1 46 40e400
1
2
3
4
5
6
7
#
问题 11
答案
- 借助掩码和移位来实现
x = ((aaa & 0xf) << 12) |
((bbb & 0xff) << 4) |
((ccc & 0x7) << 1) |
(ddd & 0x1);
1
2
3
2
3
#
问题 12
答案
如果编译器指定
int
有符号类型, 则输出为-2
如果编译器指定
int
无符号类型, 则输出为2
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
指定int
为符号类型, 测试代码如下#include <stdio.h> int main(void) { struct { int a : 2; } x; x.a = 1; x.a += 1; printf("%d\n", x.a); printf("%u\n", x.a); }
1
2
3
4
5
6
7
8
9
10
11
12
13输出
-2 4294967294
1
2- 其中
4294967294 = 2^32 - 2
- 其中
#
问题 13
答案
对
x.b
赋值会完全覆盖掉x.a
的值对
x.c
赋值- 在小端字节序的机器上, 会覆盖掉
x.b
的低位字节 - 在大端字节序的机器上, 会覆盖掉
x.b
的最高位字节
- 在小端字节序的机器上, 会覆盖掉
小端字节序上的运行结果
#include <stdio.h> int main(void) { union { int a; float b; char c; } x; x.a = 25; x.b = 3.14; x.c = 'x'; printf("%d %g %c\n", x.a, x.b, x.c); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15输出
1078523256 3.13998 x
1
#
问题 14
答案
- 用与储存数据时所用成员的相同成员进行读取
#
问题 15
答案
- 原代码只用储存指向字符串的指针, 但是修改后的代码则需要储存整个字符串
- 如果
MAX_STRING_LENGTH
的值过大, 则当储存int
或float
时会造成较多的内存浪费
# 编程练习
#
编程练习 1
答案
enum PN_TYPE
{
CALLED,
CALLING,
BILLED
};
struct PHONE_NUMBER
{
short area;
short exchange;
short station;
};
struct LONG_DISTANCE_BILL
{
short month;
short day;
short year;
int time;
struct PHONE_NUMBER numbers[3];
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#
编程练习 2
答案
将相同的部分从联合中提取出来
struct INFO { char custom_name[21]; char custom_address[41]; char model[21]; float msrp; float selling_price; enum { PURE_CASH, CASH_LOAN, LEASE } type; union { struct { float sales_tax; float licensing_fee; } pure_cash; struct { float sales_tax; float licensing_fee; float down_payment; int loan_duration; float interest_rate; float monthly_payment; char bank[21]; } cash_loan; struct { float down_payment; float security_deposit; float monthly_payment; int lease_term; } lease; } info; };
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
35
36
37
38
39
40
41
42
43
44
#
编程练习 3
答案
代码
typedef union { unsigned short addr; struct { unsigned short opcode : 10; unsigned short dst_mode : 3; unsigned short dst_reg : 3; } sgl_op; struct { unsigned short opcode : 4; unsigned short src_mode : 3; unsigned short src_reg : 3; unsigned short dst_mode : 3; unsigned short dst_reg : 3; } dbl_op; struct { unsigned short offset : 8; unsigned short opcode : 8 } branch; struct { unsigned short opcode : 7; unsigned short src_reg : 3; unsigned short dst_mode : 3; unsigned short dst_reg : 3; } ret_src; struct { unsigned short opcode : 16; } misc; } machine_inst;
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
35
36
37
38
39
编辑 (opens new window)
上次更新: 2022/01/26, 21:01:00