C语言结构体大小分析

news2024/11/15 8:49:54

一,基本类型

C语言自带的数据类型大小如下

数据类型大小(字节)
char1
short2
int4
long4或8
float4
double8
long double16

二,自定义类型---struct

C语言除了以上这些基本类型,还支持用户自己定义数据类型

类似于一下形式:

struct Student{
    char name[10];//学生姓名
    int age;//学生年龄
};

这里自定义了一个struct Student 类型的数据类型,包含有字符数组整形两种内容。

三,自定义结构体大小分析

1.错误示范

如果直接按照结构体内的成员类型相加,那么如上struct Student类型的大小应该是

10+4=14

似乎没什么问题,但是,当我们用以下代码做测试时,却发现出了问题

#include"stdio.h"
#include"stdlib.h"
struct Student{
    char name[10];//学生姓名
    int age;//学生年龄
};
int main()
{
    printf("%d\n",sizeof(struct Student));//测试struct Student大小
    system("pause");
    return 0;
}

运行结果

可以发现,结构体大小并非结构体成员大小简单相加

为什么会出现这样的结果?这是因为C语言中存在一种行为叫结构体内存对齐。

2.内存对齐

结构体成员在内存存放时,编译器会在结构体成员之间添加填充字节,以保证结构体成员的对齐要求。

2.1对齐规则

1)结构体第一个成员永远放在0偏移处

在C语言中,结构体第一个成员的地址和整个结构体的初始地址是相同的,也就是说,结构体的第一个成员始终位于结构体的初始地址处。

可以用以下代码证明:

#include"stdio.h"
#include"stdlib.h"
struct student {
    char name[20];
    int age;
    float score;
};
int main()
{
    struct student stu;
    printf("%p\n%p\n",&(stu),&(stu.name));
    system("pause");
    return 0;
}

运行结果如下(不同设备上运行的数值可能不相同,但是一台设备上两行的数据相同):

可以看到,结构体变量的地址和结构体变量第一个成员的地址是相同的。

2)从第二个成员开始,以后的每个成员的地址距离都要对齐到某个对齐数的整数倍处,这个对齐数是:(成员自身大小和默认对齐数)的较小值

这句话是什么意思呢,让我们先看一个例子:

#include"stdio.h"
struct S
{
    char a;
    int b;
    char c;
    long long d;
}s;//创建结构体变量s
int main()
{
    printf("结构体大小:%d\n",sizeof(struct S));
    printf("各个成员的地址:\n");
    printf("%p char a\n",&(s.a));
    printf("%p int b\n",&(s.b));
    printf("%p char c\n",&(s.c));
    printf("%p long long d",&(s.d));
    return 0;
}

运行结果如下:

需要注意的是,%p是以16进制的格式进行输出,最后一个long long 型数据d的大小为8字节,则其结束地址应该是7d057

因此,结构体大小:7d057(16)-7d039(16)=18(16)=24(10)

接下来我们将以excel表格代表内存空间,分析每个成员在内存的分布

其中,D列的0到23代表每个地址距离起始地址的偏移量

@1 首先是 char a,已知结构体第一个成员永远放在0偏移处,且char 只占1字节,那么a在内存的分布暂时是这样的

@2 接下来是int b,第二个成员要对齐到对齐数的整数倍,也就是说,它的起始地址的偏移量必须是对齐数的整数倍。

对齐数:1)数据类型自身的对齐数:char型数据自身对齐值为1字节,short型数据为2字节,int/float型为4字节,double型为8字节。

2)默认对齐数:VS2013默认对齐数为8,或#pragma pack (value)时的指定对齐值value。

3)数据成员、结构体的有效对齐数:自身对齐值和指定对齐值中较小者,即有效对齐值=min{自身对齐值,当前指定的pack值}。

*需要注意的是gcc无默认对齐数

由于我使用的是gcc编译器,则int b的对齐数是其自身对齐值4,如果使用的是Visual Studio这个IDE,那么其对齐数为min(4,8)=4

因此,编译器将会在成员char a后再填补3个字节,使int b对齐,内存分布如下:

@3 接下来是char c,其对齐数是min(1,8)=1,大小是1字节,那么,直接接在偏移量为8的地方

@4 最后一个成员是long long d,对齐数是8,大小8字节,则需要对齐到8*2=16这个偏移量的地址,并在char c占用内存后填补7个字节,内存分布如下:

3)结构体大小是所有成员对齐数中最大对齐数的整数倍

当最后一个结构体成员存放后,如果结构体大小不是所有成员的对齐数中最大对齐数的整数倍,那么会在结构体最后的成员后补字节。

#include"stdio.h"
struct S{
    int a;
    char c;
}s;
int main()
{
    printf("%d struct S\n",sizeof(struct S));
    printf("%p int a\n",&(s.a));
    printf("%p char c\n",&(s.c));
    return 0;
}
return 0;
}

接下来我们分析结构体成员是如何分布在内存中的

@1 首先是int a,对齐数为4,大小是4个字节,由于是第一个成员,直接放在0偏移处

@2 其次是char c,对齐数为1,大小是一个字节,则可以存放在偏移量为4的地方

@3 我们发现,如果结构体到这里就分配结束,那么结构体大小应该为5,但是实际情况却是结构体大小为8。实际上结构体也要进行内存对齐。

此结构体中int a和char c的对齐数分别为4和1,结构体对齐数是成员对齐数中的最大对齐数,则此结构体对齐数大小MAX(4,1)=4,那么,就需要在char c后填补字节到结构体大小为8.

4)嵌套结构体中子结构体对齐到子结构体自己成员的最大对齐数的整数倍

@1 offsetof(type, member-designator) 求偏移量宏

此库宏需要包含头文件“stddef.h”,会生成一个类型为size_t的整形数,代表该成员在内存中距离起始地址的偏移量。

实例可参考:C 库宏 – offsetof() | 菜鸟教程 (runoob.com)

话不多说,上代码:

#include"stdio.h"
#include"stddef.h"
struct stu{
    int name;
    double grades;
};
struct team{
    char name[6];
    struct stu Students;
    int num_stu;
};
int main()
{
    struct team Team;
    printf("%d struct stu\n",sizeof(struct stu));
    printf("%d struct team\n",sizeof(struct team));
    printf("%p char name[10]\n"
    "%p struct stu Students\n"
    "%p int num_stu\n",(Team.name),&(Team.Students),&(Team.num_stu));
    printf("%d struct stu Students\n%d int num_stu\n",offsetof(struct team,Students),offsetof(struct team,num_stu));
    return 0;
}

运行结果如下:

@2 根据上面三个规律,可以得出struct stu的大小是16,接下来我们看看struct team的成员是怎么分布的

@3 首先是char name[6],第一个成员直接占用6字节,我知道你们都懂😊

@4 然后来到了我们的重头戏,struct stu,首先,这个子结构体成员最大的对齐数是8(double),所以,需要对齐到偏移量为8的地方,符合offsetof(struct team,Students) 返回值为8的结果。因此,还需要在第一个成员后补两个字节,子结构体大小为16,占用16个字节

@5 最后一个成员int num_stu,占用4个字节,对齐数为4,24=4*6,所以对齐到24偏移量处。最后一个成员放进去后,结构体大小为28,不等于结构体对齐数(成员的最大对齐数)8的整数倍,所以,需要再最后一个成员后补上4个字节,让结构体大小为32=8*4.

2.2对齐数的设置

编译时的默认对齐数可以通过#pragma pack(n) 来设置,它会影响结构体中每个成员的有效对齐值,有效对齐值是编译器的对齐数和成员大小中较小的那个。例如,如果编译器的对齐数是4,而结构体中有一个double类型的成员(占8字节),那么该成员的有效对齐值是4,而不是8。

上代码:

@1 首先我们看看不带#pragma pack(n)的:

#include"stdio.h"
#include"stdlib.h"
struct stu{
	int age;
	double grades;
};
int main()
{
	printf("%d\n",sizeof(struct stu));
	return 0;
}

先分析一下,int age占用四个字节,double grades对齐数为8,则需要补4个字节到int age,double grades自己占用8个字节,所以结构体大小为16个字节,看看运行结果吧:

果然如此,那加上prama pack(4) 呢。

@2 带pragma pack(4)

上代码(其实只是多了一个#pragma pack(4)而已😏):

#pragma pack(4)
#include"stdio.h"
#include"stdlib.h"
struct stu{
	int age;
	double grades;
};
int main()
{
	printf("%d\n",sizeof(struct stu));
	return 0;
}

运行结果:

现在编译器对齐数为4。首先,int age占用4个字节,然后,double grades 对齐数=MIN(自己大小8,编译器对齐数4)=4,所以不用在age后补字节,grades直接从偏移量为4的地方开始。两个成员放完后,结构体大小为12,结构体对齐数为MAX(成员们的对齐数)=4,12=4*3,则无需补字节。


感谢你阅读我的博客,如果你对我的内容有任何的意见、建议或者问题,欢迎在评论区留言,我会尽快回复。如果你发现了我的错误或者疏漏,也请不吝指正,我会及时修改。希望我的博客能对你有所帮助,也期待与你的交流和分享。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/801401.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android Banner - ViewPager

现在来给viewpager实现的banenr加上自动轮播 自动轮播的原理,使用handler的延迟消息来实现。 自动轮播实现如下内容 开始轮播&停止轮播 可配置轮播时长、轮播方向 通过自定义属性来配置轮播时长,方向 感知生命周期,可见时开始轮播&…

【Ansible】Ansible自动化运维工具的应用与常用命令

ansible自动化运维工具 一、ansible 的概述1. ansible 的概念2. ansible 的特性 二、ansible 的部署与命令1. ansible 的部署1.1 服务器ip地址设置1.2 ansible 服务器部署 2. ansible 命令行模块2.1 command 模块2.2 shell 模块2.3 cron 模块2.4 user 模块2.5 group 模块2.6 co…

Spring Batch教程(三)示例:从mysql中读取数据写入文本和从多个文本中读取内容写入mysql

Spring batch 系列文章 Spring Batch教程(一) 简单的介绍以及通过springbatch将xml文件转成txt文件 Spring Batch教程(二)示例:将txt文件转成xml文件以及读取xml文件内容存储到数据库mysql Spring Batch教程&#xff…

在CSDN的几年,我的Python书籍,第三次重印了!

大家好,我是黄伟🤭 我的书籍《快学Python:自动化办公轻松实战》第3次重印了,感谢大家的支持。 这本书干货多多,零基础教你Python自动化办公,让你工作效率提升10倍,提前1小时下班。 因此&#…

2018年全国硕士研究生入学统一考试管理类专业学位联考写作试题——解析版

2018年1月真题 四、写作:第56~57小题,共65分。其中论证有效性分析30 分,论说文35分。 56.论证有效性分析: 分析下述论证中存在的缺陷和漏洞,选择若干要点,写一篇600字左右的文章,对该论证的有…

「开源项目」现代化开源Linux服务器运维管理面板-1Panel

1Panel 基本介绍 1Panel 是新一代的 Linux 服务器运维管理面板 产品优势 快速建站:深度集成 Wordpress 和 Halo,域名绑定、SSL 证书配置等一键搞定; 高效管理:通过 Web 端轻松管理 Linux 服务器,包括应用管理、主机监控…

二分图算法(染色法 匈牙利算法)

目录 二分图算法总览二分图的概念1.二分图的定义2.二分图的特点3.二分图的应用 染色法(判断二分图)算法步骤算法运用染色法判定二分图 匈牙利算法(计算二分图的最大匹配)二分图的匹配算法步骤算法应用二分图的最大匹配 二分图算法…

Jmeter并发测试

基本步骤 1、新建线程组 测试计划右键——>添加——>线程(用户)——>线程组 2、 添加HTTP请求 线程组右键——>添加——>取样器——>HTTP请求 3、 添加HTTP信息头管理器 线程组右键——>添加——>配置元件——>HTTP信息头…

【观察】以超融合创新架构,加速企业应用现代化

我们知道,数字化转型的不断加速,核心就是应用的加速。在整个数字化体系中,软件应用是让一切发挥价值的落地路径。在应用发挥能力之前,企业需要进行大量软硬件准备以及应用开发工作;在应用开始发挥能力之,企…

牛客小白月赛75F题解

文章目录 [ 打牌](https://ac.nowcoder.com/acm/contest/60063/F)问题建模问题分析代码 打牌 问题建模 给出三个长度为3的字符串,每个字符串仅由’w’,‘i’,‘n’三种字符组成,以及回合数n,每一个回合每一个字符串选择一个字符向其下一个字…

【Android】底层逻辑深入了解(学习笔记)(未完)

step by step. 目录 init启动 Zygote进程: SystemServer处理过程 Binder: Launcher启动过程 Android系统启动流程 四大组件 Activity Service BroadcastReceiver广播 ContentProvider内容提供者(进程内和进程间的数据共享&#xff…

Latex | 使用MATLAB生成.eps矢量图并导入Latex中的方法

一、问题描述 用Latex时写paper时,要导入MATLAB生成的图进去 二、解决思路 (1)在MATLAB生成图片的窗口中,导出.eps矢量图 (2)把图上传到overleaf的目录 (3)在文中添加相应代码 三…

深度学习100例 | 第31天-卷积神经网络(DenseNet)识别生活物品

🚀 我的环境: 语言环境:Python3.6.5编译器:jupyter notebook深度学习环境:TensorFlow2.4.1显卡(GPU):NVIDIA GeForce RTX 3080数据:📌【传送门】 &#x1f…

Gradle和Maven的区别

Gradle和Maven 当涉及到构建和管理项目时,Gradle和Maven是两个非常流行的选项。本文将讨论Gradle和Maven之间的区别以及它们的配置信息差异。 1. Gradle和Maven的区别 1.1 构建脚本语言 Maven使用XML作为构建脚本语言,而Gradle使用基于Groovy的DSL&…

HashMap查找

文章目录 1 哈希表的基本概念1.1 两个例子1.2 如何查找1.3 若干术语 2 哈希函数的构造方法2.1 直接定址法2.2 除留余数法 3 处理冲突的方法3.1 开放地址法3.1.1 线性探测法3.1.2 二次探测法3.1.3 伪随机探测法 3.2 链地址法(拉链法)3.2.1 创建步骤3.2.2 …

STM32-风速传感器(ADC)

目录 0 说明 1 传感器介绍 2 代码说明 2.1 ADC.c 2.2 adc.h 2.3 main.c 0 说明 本篇文章主要是说明怎么使用STM32单片机读取风速传感器采集到的数据,读取方式是ADC,并且附带着STM32所需要的全部代码,所使用的风速传感器如下图所示。 附&am…

Redis—相关背景

Redis—相关背景 🔎Redis—特性In-memory data structures—在内存中存储数据Programmability—可编程性Extensibility—可扩展性Persistence—持久化Clustering—集群High availability—高可用 🔎Redis 为什么快🔎Redis 的使用场景Real-tim…

看看ChatGPT的Embedding接口都完成哪些任务

调用Embedding接口完成文本分类 前面博客介绍了如何调用ChatGPT的Embedding接口完成文本聚类任务,实现过程入下图所示: 除了完成文本分类,调用Embedding接口还可完成聚类任务。 调用Embedding接口完成聚类任务 聚类任务是一种无监督学习任…

tinymce实现将word中内容(文字图片等)直接粘贴至编辑器中——利用插件tinymce-powerpaste-plugin

TinyMCE是一款易用、且功能强大的所见即所得的富文本编辑器。同类程序有:UEditor、Kindeditor、Simditor、CKEditor、wangEditor、Suneditor、froala等等。 TinyMCE的优势: 开源可商用,基于LGPL2.1 插件丰富,自带插件基本涵盖日常…

活字格性能优化技巧——如何利用数据库主键提升访问性能

大家都知道,活字格作为企业级低代码开发平台,拥有6大引擎,3大能力,能够高效落地企业级应用。在每年的应用大赛中也能看到很多格友利用活字格做了很多复杂的应用,例如2021年企业级低代码应用大赛中宁波聚轩利用活字格做…