当前位置: 首页 > article >正文

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;
};

http://www.kler.cn/a/323399.html

相关文章:

  • HBase压测 ycsb
  • 数据仓库在大数据处理中的作用
  • pytest在conftest.py中实现用例执行失败进行截图并附到allure测试报告
  • 我的docker随笔45:在龙芯平台安装docker
  • GitHub新手入门 - 从创建仓库到协作管理
  • 03-axios常用的请求方法、axios错误处理
  • 算法打卡:第十一章 图论part08
  • 什么是算力?cpu+显卡吗?
  • 【JAVA-数据结构】时间和空间复杂度
  • ubuntu中通过源码安装pointnet2_ops_lib
  • 360周鸿祎为什么说大模型已成茶叶蛋?
  • html+css+js实现Progress 进度条
  • 差速轮纯跟踪算法
  • 设备管理平台-支持快速开发
  • Woocommerce怎么分类显示产品?如何将Shopify的产品导入到Woocommerce?
  • 如何恢复被删除的 GitLab 项目?
  • git rebase 调整提交顺序
  • springboot 实现用户登录身份验证
  • 【NLP】daydayup 词向量训练模型word2vec
  • Maven中 <parent > 的<version>可以使用变量吗
  • Unity3D入门(四) : Android和Unity3D交互 - Unity调用Android
  • FreeRTOS 内存管理源码解析
  • 数据结构:线性表的链式表示
  • 中国农业银行——开源软件一体化管理平台
  • 《AI办公类工具表格处理系列之一——办公小浣熊》
  • 逃离陷阱:如何巧妙避免机器学习中的过拟合与欠拟合