63.【C语言】再议结构体(上)

1.复习

20.【C语言】初识结构体(重要)

48.【C语言】结构体补充

2.结构体的特殊声明

01.匿名结构体

*定义

不完全声明,即结构体没有自己的名字(没有结构体标签)

*注意事项

1.匿名结构体只能使用一次

2.下列代码合法吗
struct
{
	int a;
	char b;
	float c;
}x;

struct
{
	int a;
	char b;
	float c;
}*p;

int main()
{
	p = &x;
	return 0;
}

虽然编译没有问题 ,但生成解决方案会报警告

 warning C4133: “=”: 从“*”到“*”的类型不兼容

编译器认为:x;和*p;是两个不同的结构体

02.自引用

前置知识

*数据结构:数据在内存中的存储结构

*常见的数据结构:数组,记录,链表

*一个典型的数据结构----链表:一种物理存储单元上非连续(因为用了指针)、非顺序(因为用了指针)的存储结构,用于线性方式存储数据(对比数组:元素的顺序集合)

下面这张图(摘自《计算机科学导论》)体现非顺序65-->66-->72-->96-->85-->74

*链表中的每一个元素称为结点,结点包含两个部分:数据和链(链是下一个节点的地址(指针),指向下一个节点的数据),链最后一个元素包含一个空指针

*链表名是头指针的名字,下面这张图(摘自《计算机科学导论》)

《计算机科学导论》对链表的解释


单向链表图例

代码实现链表的一个结点

struct Node
{
    int data = 0;//数据
    struct Node* next;//链(地址)(指针)
};

注:Node n.节点,结点

运行上方代码,x86环境下在内存中查看结构体

typedef重命名结构体

禁止使用typedef重命名匿名结构体

typedef struct Node
{
    int data;
    struct Node* next;
}Node;

3. 结构体内存对齐(考频高)

如果看过李忠老师的《x86汇编语言:从实模式到保护模式 第二版》将会比较好理解

8086CPU的内存对齐方式

书中提到:8086CPU在存储数据段时采用16字节对齐的方式,如果定义的数据没有到16字节的整数倍,将以00填充满

例如:

db "Hello"
db "World!"

在内存中查看数据的排布:

设该字符串有x个字符(即x bytes)

其在内存中的存储占用的字节数S的公式:

① x ≦ 16,S=16

②x = 16k (k为正整数),S=16k

③  16k<x ≦16(k+1) (k为正整数),S=16(k+1)

对于结构体来说,内存对齐有4条规则

1. 结构体的第一个成员对齐到和结构体变量起始位置偏移量为0的地址处

2. 其他成员变量(即从第二个成员变量开始)要对齐到某个数字(对齐数)的整数倍的地址处

    对齐数 = 编译器默认的一个对齐数与该成员变量大小的较小值

    对齐数要看偏移量

           -->VS中默认的值为8 bytes

           -->Linux中gcc没有默认对齐数,对齐数就是成员自身的大小

3. 结构体总大小(注意这里说的不是偏移量)为最大对齐数(结构体中每个成员变量(含第一个成员变量)都有一个对齐数,所有对齐数中最大的)的整数倍,这样可能会浪费一些空间

4.如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。

例题:非嵌套结构体

求下列代码的执行结果(在VS2022中测试)

#include <stdio.h>
struct s1
{
	char a;
	char b;
	int c;
};

struct s2
{
	char a;
	int c;
	char b;
};

int main()
{
	printf("%zd,%zd", sizeof(struct s1), sizeof(struct s2));
	return 0;
}

提示:结构体s1和结构体 s2唯一不同的地方在成员变量的排列顺序

s1:①char a ②char b ③int c

s2:①char a ②int c ③char b

 答案速查

分析

3.【C语言】内置数据类型文中讲过char占1个字节,int占4个字节

由内存对齐有4条规则可以画图(设CPU为结构体从偏移量为0处开辟空间):

对于结构体s1

1 < 8,a的对齐数为1

4 < 8,c的对齐数为4

从偏移量0到偏移量7,一共占8个字节,为最大对齐数4的整数倍(对齐数1 < 对齐数4)

因此sizeof(struct s1) == 8

对于结构体s2

c存储的位置必须从偏移量为4的最近位置开始

存储完a,c,b后,8-0+1=9 bytes,不是最大对齐数4的整数倍,因此要至偏移量为11的位置

因此打印结果为8,12

练习

求下列代码的执行结果(在VS2022中测试)

#include <stdio.h>
struct s3
{
	double d;
	char c;
	int i;
};

int main()
{
	printf("%zd", sizeof(struct s3));
	return 0;
}

答案速查

分析

例题:嵌套结构体

求下列代码的执行结果(在VS2022中测试)

#include <stdio.h>
struct s3
{
	double d;
	char c;
	int i;
};

struct s4
{
	char c1;
	struct s3 s3;
	double d;
};

int main()
{
    struct s4 s4;
	printf("%zd", sizeof(struct s4));
	return 0;
}

答案速查

分析

结构体的整体大小就是所有最大对齐数的整数倍(s3的最大对齐数和s4的最大对齐数都是8)

4.内存对齐的原因

1. 平台原因(移植原因)
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常
2. 性能原因
数据结构(尤其是栈)应该尽可能地在自然边界上对齐.原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问.假设一个处理器总是从内存中取8个字节,则地址必须是8的倍数.如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用一个内存操作来读或者写值了.否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中
总体来说:结构体的内存对齐是拿空间来换取时间的做法

5.内存对齐节省空间的方法

让占用空间小的成员尽量集中在一起

例如:

struct S1
{
	char c1;
	int i;
	char c2;
};
struct S2
{
	char c1;
	char c2;
	int i;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值