title: C语言结构体大小分析
author: saopigqwq233
date: 2022-04-05

C语言结构体大小分析

一,基本类型

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

数据类型 大小(字节)
char 1
short 2
int 4
long 4或8
float 4
double 8
long double 16

二,自定义类型---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,则无需补字节。


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

C语言结构体大小分析的更多相关文章

  1. 关于C语言中结构体大小计算

    结构体大小的计算,.网上说法一大堆还都不一样分什么对齐不对齐,偏移量什么的.. 在此稍微举例简单总结下: 对齐原则:每一成员的结束偏移量需对齐为后一成员类型的倍数  补齐原则:最终大小补齐为成员中最大 ...

  2. struct结构体大小的计算(内存对齐)

    本次实验环境 环境1:Win10, QT 5.12 一. 背景 当普通的类型无法满足我们的需求的时候,就需要用到结构体了.结构体可衍生出结构体数组,结构体还可以嵌套结构体,这下子数据类型就丰富多彩了, ...

  3. C/C++ sizeof函数解析——解决sizeof求结构体大小的问题

    C/C++中不同数据类型所占用的内存大小 32位                 64位 char               1                    1 int           ...

  4. Windows下struct和union字节对齐设置以及大小的确定(一 简介和结构体大小的确定)

    在windows下设置字节对齐大小的方式,目前我了解有三种: 1. 在编译程序时候的编译选项  /Zp[n],如 cl /Zp4 表示对齐大小是4字节: 2. 预处理命令   #pragma pack ...

  5. C++字节对齐与结构体大小计算

    转载注明出处:http://pppboy.blog.163.com/blog/static/30203796201082494026399/ 感谢原创博主的辛勤成果. 说明: 结构体的sizeof值, ...

  6. C语言中结构体大小计算

    1.普通结构体 struct student { char sex; char a; char b; int age; char name[100]; }; 该结构体大小为108 解答:1.先算str ...

  7. C语言地址对齐(转)--网络编程之结构体大小的计算

    什么是地址对齐? 现代计算机中内存空间都是按照字节(byte)划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各类型数 ...

  8. 从gcc的__attribute__((packed))聊到结构体大小的问题

    公司的前辈的代码里面 结构体的花括号最后 有__attribute__((packed))字样.以前没见过,所以查了查.学习学习http://blog.sina.com.cn/s/blog_559f6 ...

  9. sizeof进行结构体大小的判断

    typedef struct{    int a;    char b;}A_t;typedef struct{    int a;    char b;    char c;}B_t;typedef ...

  10. c语言结构体小知识

    引自:http://c.biancheng.net/cpp/html/88.html 结构体在内存中是连续存储的 struct stu{ char *name; //姓名 int num; //学号 ...

随机推荐

  1. C#中的范围类型(Range Type)

    //语法糖Rangestatic void Main(string[] args) { var myArray = new string[] { "Item1", "It ...

  2. operations使用研究

    简介 operations支持在peer或者orderer运行过程中,提供基于restful接口的运维服务.包括健康检查.日志level管理.指标metrics接口等.首先利用test-network ...

  3. springboot jodconverter openoffice 实现 office 文件 在线预览

    这个已是好几个月前做的,好多细节已记得不那边清楚了.今天部署到环境还遇到不少问题.总结下. 1.office 文件实现在线预览的一般思路就是,将文件转pdf,在通过pdf.js 实现在线浏览.这其中转 ...

  4. jmeter的三种参数化方式

    一.通过添加前置处理器(用户参数) 1. 在http层级下添加--前置处理器--用户参数 2.可以修改名称,每次迭代更新一次(一定要勾选上),这样才会每次迭代变量值也更新 ,点击下面添加用户(多次测试 ...

  5. mac 安装go语言以及配置环境变量

    Go官网下载地址:https://golang.org/dl/ Go官方镜像站(推荐):https://golang.google.cn/dl/ 其他版本自己根据系统版本下载,这里只介绍mac下载 一 ...

  6. Jquery ajax参数设置(转)

    参数名 类型 描述 url String (默认: 当前页地址) 发送请求的地址. type String (默认: "GET") 请求方式 ("POST" 或 ...

  7. 【Rust入门】(一)构建自己的第一个Rust项目

    安装Rust 参考文档,指定安装目录和镜像配置. rustc --version 检查是否安装成功. 构建程序 使用rustc编译运行 rustc 编译:rustc main.js 运行: Windo ...

  8. C# 根据 RichTextBox 内容 动态 重设其大小 以达到 不会 显示滚动条

    /// <summary> /// 根据内容重设大小以达到不会显示滚动条 /// 测试:正确 /// 时间:202106021957 /// </summary> public ...

  9. 实验六 开源控制器实践——RYU

    (一)基本要求 搭建SDN拓扑,协议使用Open Flow 1.0,并连接Ryu控制器,通过Ryu的图形界面查看网络拓扑. 阅读Ryu文档的The First Application一节,运行当中的L ...

  10. kettle连接oracle

    连接oracle 10g 驱动classes12.jar 配置一下三项即可: 1.数据库名称:ip:端口/实例 2.用户名 3.密码