C语言热门考点结构体与内存对齐详解

目录
  • 一、引例
    • 1.结构体的第一个成员永远放在结构体起始位置偏移量为0的位置
    • 2.从第二个成员开始,总是放在偏移量为一个对齐数的整数处,对齐数=编译器默认的对齐数和变量自身大小的较小值
    • 3.结构体的总大小必须是各个成员的对齐数中最大的那个对齐数的整数倍
  • 二、小试牛刀
  • 三、嵌套结构体的特殊情况
  • 四、关于为什么存在内存对齐
    • 1.平台原因(移植原因):
    • 2.性能原因:
  • 总结

一、引例

到底什么是结构体内存对齐,我们用一段代码来介绍一下

struct S1
{
	char c1;//1字节
	int a;//4字节
	char c2;//1字节
};
int main()
{
	printf("%d\n", sizeof(struct S1));
	//这里打印12
}

先来解释S1,结构体S1中有2个char类型,1个int类型。那按道理应该是占2*1+4=6个字节啊,为什么打印的是12呢?到这里,我们必须要来了解一下结构体内存对齐的规则:

1.结构体的第一个成员永远放在结构体起始位置偏移量为0的位置

对于偏移量你可以这样理解:数组下标为0的相对它自己偏移量为0,下标为1的相对下标为0的偏移量为1…
举例说明:

S1第一个成员是c1,它会被放在结构体起始位置偏移量为0的位置,如下图红色部分

2.从第二个成员开始,总是放在偏移量为一个对齐数的整数处,对齐数=编译器默认的对齐数和变量自身大小的较小值

对齐数=min(编译器默认的对齐数,变量自身大小)
Linux-没有对齐数,VS下对齐数默认为8

我们仍以S1这个结构体进行举例,结构体第二个成员是int类型的a,占4个字节,笔者VS环境下默认对齐数是8,取两者较小值是4,那a应该放到偏移量为4的倍数上

放到4的倍数上也就说可以放在偏移量为4这里,偏移量为1,2,3的这3个空间就白白被浪费了。而a是int型占4个字节,所以会一直占用到偏移量为7的位置。

接下来是结构体的第三个成员,char类型的c2,c2占1个字节,VS环境下默认对齐数是8,取较小值为1,也就是说只要是1的倍数的偏移量都可以放,我们紧接着放在a后面,也就是偏移量8的位置

那到这里结构体3个成员都用完了啊,只有8个啊,为什么打印是12呢?这里就要涉及结构体内存对齐的第3个规则

3.结构体的总大小必须是各个成员的对齐数中最大的那个对齐数的整数倍

我们由前面讲解知道结构体三个成员c1,a,c2对齐数分别为1,4,1这三个中最大对齐数是4,总大小要为4的整数倍,那这时候肯定有小伙伴会问:我们现在不是对齐到8了嘛,8不是4的倍数吗?注意!这里说的是空间总大小,而8是所谓的偏移量,偏移量是从0开始算的,到8已经有9个空间了,所以我们这里空间要到12,也就是偏移量到11

(后面加上的三个空间用不到,但是由于规定还是算在结构体总空间内)

二、小试牛刀

我们再来看一道类似的题目

代码如下(示例):

struct S2
{
	char c1;//1字节
	char c2;//1字节
	int a;//4字节
};
int main()
{
	printf("%d\n", sizeof(struct S2));
	//这里打印8
}

首先第一个结构体成员是char类型的c1,由规则1,它会直接被放在偏移量为0的位置
(图示灰色部分)

第二个成员是char类型的c2,占1字节,VS下默认对齐数是8,取较小值是1,只要放在偏移量为1的倍数上即可(任意位置),紧跟着0,放在偏移量为1处(图示红色部分)

最后一个成员int类型的a,占4个字节,VS环境下默认对齐数是8,取较小者4,放在偏移量为4的整数倍处,也就是4这里,然后由于int占4个字节所以一直占用到偏移量7处

再来看看规则3,结构体的总大小必须是各个成员的对齐数中最大的那个对齐数的整数倍,也就是4的倍数,我们现在正好是占8个空间,8正好是4的倍数,所以就不用再往下浪费空间了,打印出8

三、嵌套结构体的特殊情况

代码如下(示例):

struct S3
{
	double d;//double占8字节,默认对齐数8,取较小值,对齐数8
	char c;//对齐数1
	int i;//对齐数4
};
struct S4
{
	char c1;
	struct S3 s3;
	double d;
};
int main()
{
	printf("%d\n", sizeof(struct S4));
}

关于结构体S3我们可以采用和前面S1、S2一样的方法计算出来是占16个字节空间,我们这里重点讨论S4,对S3有兴趣的小伙伴可自行求解。

S4中的第一个成员c1,按规则1直接放在偏移量为0处,第二个成员s3怎么办呢?这里涉及结构体内存对齐的第四个规则:

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

s3这个结构体三个成员最大对齐数是8,也就是要对齐到偏移量为8的倍数处,然后s3是占16个字节,所以一直占到偏移量23处(s3结构体对齐数是本身s3结构体三个成员中最大对齐数)

ps:在VS环境中,嵌套结构体的最大对齐数超过8,仍然用8做最大对齐数(比默认对齐数大了,取较小值就取默认对齐数了)

S4最后一个成员double类型的d占8字节,默认对齐数8,对齐数取8,然后放在偏移量为对齐数的整数倍处,正好往下放在24处,本身占8字节所以占到31

偏移量0-31共占32字节,S4中的成员c1,s3,d对齐数分别为1,8,8所以最大对齐数是8,32恰是8的倍数,所以这里不用再浪费空间来满足 “结构体的总大小必须是各个成员的对齐数中最大的那个对齐数的整数倍”这个规则,结构体总大小就是32

四、关于为什么存在内存对齐

1.平台原因(移植原因):

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定的类型的数据,否则抛出硬件异常

2.性能原因:

数据结构(尤其是栈),应尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅仅需要1次
总体来说:结构体的内存对齐是用空间换时间

总结

本文介绍了结构体内存对齐的四大规则,并举例说明了如何进行方法的操作,对于其中特殊的嵌套结构体内存对齐也进行了相应讲解,希望读者在学习完本文后能对结构体内存对齐有一个系统的认识。祝读者学业有成!

更多关于C语言结构体与内存对齐的资料请关注我们其它相关文章!

(0)

相关推荐

  • 一篇文章带你了解C语言内存对齐解决的问题

    目录 一.内存对齐为4个字节的好处 二.内存对齐的目的是以空间换取速度 2.1.内存对齐为4的例子 2.2.内存没有使用内存对齐的例子 CPU读取数据的过程: 三.掌握内存对齐的必要性 总结 一.内存对齐为4个字节的好处 首先,了解一下CPU从内存里读取数据的流程: 第一步,CPU通过地址总线,找到该数据的位置. 第二步,通过控制总线,发送读取数据的指令. 第三步,通过数据总线,从内存里获取该数据的内容. 内存对齐使用4个字节的原因有: 1.STM32单片机的数据总线与地址总线都是32bit(4

  • 深入理解C语言内存对齐

    一.内存对齐的初步讲解 内存对齐可以用一句话来概括: "数据项只能存储在地址是数据项大小的整数倍的内存位置上" 例如int类型占用4个字节,地址只能在0,4,8等位置上. 例1: 复制代码 代码如下: #include <stdio.h>struct xx{        char b;        int a;        int c;        char d;}; int main(){        struct xx bb;        printf(&q

  • c语言结构体字节对齐的实现方法

    目录 1.什么是字节对齐 2.为什么要有字节对齐 3.手动设置对齐 4.结构体比较方法 1.什么是字节对齐 在c语言的结构体里面一般会按照某种规则去进行字节对齐. 我们先看一段代码: struct st1 { char name; double age; char sex; }; //32位下 sizeof(struct st1) = 16 //64位下 sizeof(struct st1) = 24 struct st2 { char a; char b; char c; }; //32位和6

  • C语言中结构体与内存对齐实例解析

    1.结构体类型 C语言中的2种类型:原生类型和自定义类型,结构体类型是一种自定义类型. 2.结构体使用时先定义结构体类型再用类型定义变量 -> 结构体定义时需要先定义结构体类型,然后再用类型来定义变量. -> 也可以在定义结构体类型的同时定义结构体变量. // 定义类型 struct people { char name[20]; int age; }; // 定义类型的同时定义变量. struct student { char name[20]; int age; }s1; // 将类型st

  • C语言中结构体、联合体的成员内存对齐情况

    前言 最近项目进行中,遇到一个小问题,在数据协议传输过程中,我为了方便解析,就定义了一个结构体,在数据的指针传入函数的时候,我用定义好的结构体进行强制转化,没想到一直解析失败,调试很久,终于反应过来,在用结构体指针对数据强制转换时,定义结构体我没有注意到数据对齐,因为在底层实现中,我传入的数据buffer是排列整齐的,而强制转化的结构体格式中,我定义的时候没有使用__attribute__((__packed__))或者__packed强制数据对齐,导致结构体成员真实排列会按照成员中最大的变量的

  • C语言热门考点结构体与内存对齐详解

    目录 一.引例 1.结构体的第一个成员永远放在结构体起始位置偏移量为0的位置 2.从第二个成员开始,总是放在偏移量为一个对齐数的整数处,对齐数=编译器默认的对齐数和变量自身大小的较小值 3.结构体的总大小必须是各个成员的对齐数中最大的那个对齐数的整数倍 二.小试牛刀 三.嵌套结构体的特殊情况 四.关于为什么存在内存对齐 1.平台原因(移植原因): 2.性能原因: 总结 一.引例 到底什么是结构体内存对齐,我们用一段代码来介绍一下 struct S1 { char c1;//1字节 int a;/

  • C语言程序中结构体的内存对齐详解

    目录 一.为什么存在内存对齐 二.结构体的内存对齐四规则 三.举例 一.为什么存在内存对齐 1.平台原因(移植原因): 不是所有的硬件平台都能访问任意地址上的任意数据的:某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常. 2. 性能原因: 数据结构(尤其是栈)应该尽可能地在自然边界上对齐. 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问:而对齐的内存访问仅需要一次访问. 总的来说结构体的内存对齐是拿空间来换取时间的做法. 二.结构体的内存对齐四规则 默认情况:默认的对

  • C语言详细分析结构体的内存对齐规则

    目录 引例 结构体内存对齐规则 那么为什么要有内存对齐呢 如何优化 修改默认对齐数 结构体的内存对齐是一个特别热门的知识点! 引例 #include<iostream> using namespace std; struct S { char c; // 1 int a; // 4 char d; // 1 }; int main() { struct S s = { 'a',2,'y'}; cout << sizeof(struct S) << endl;// 12

  • C语言 详细分析结构体的内存对齐

    目录 一.结构体 二.结构体内存对齐 1.非嵌套结构体的大小 2.含嵌套结构体的大小 三.为什么要内存对齐 1.平台原因(移植原因) 2.性能原因 一.结构体 结构体 (struct)是一种数据结构,可以包含很多数据类型,可以实现比较复杂的数据结构. 常见的int,char类型变量,我们可以一眼看出占多少字节,但对于结构体,可就有点难度了. 让我们来猜猜以下程序的输出 struct S1 { char c1; int i; char c2; }; struct S2 { char c1; cha

  • Go语言学习之结构体和方法使用详解

    目录 1. 结构体别名定义 2. 工厂模式 3. Tag 原信息 4. 匿名字段 5. 方法 1. 结构体别名定义 变量别名定义 package main import "fmt" type integer int func main() { //类型别名定义 var i integer = 1000 fmt.Printf("值: %d, 类型: %T\n", i, i) var j int = 100 j = int(i) //j和i不属于同一类型,需要转换 fm

  • C语言结构体中内存对齐的问题理解

    目录 前言 思考 结构体在内存中开辟空间时内存对齐的规则 为什么存在内存对齐 1.平台的原因 2.性能的原因 前言 学C的同学应该知道~ 想精通C语言就不得不面对—指针与内存 续上次指针的进阶,这一章我来聊一聊C语言内存对齐的问题 学习结构体的你有没有注意过结构体向系统申请的内存为多少呢的 思考 #include<stdio.h> typedef struct s1 { char a; char b; int c; }s1; typedef struct s2 { char a; int c;

  • C语言详解结构体的内存对齐与大小计算

    目录 结构体的内存对齐 1.计算结构体的大小 2.结构体的对齐规则 3.为什么存在内存对齐? 4.总结 结构体的内存对齐 1.计算结构体的大小 struct S1 { char c1; // 1 byte,默认对齐数为8,所以c1的对齐数是1,第一个成员变量放在与结构体变量偏移量为0的地址处 int i; // 4 byte,默认对齐数为8,所以i的对齐数是4,所以i要放到偏移量为 4的整数倍 的地址处 char c2; // 1 byte,默认对齐数为8,所以c2的对齐数是1,所以c2要放到偏

  • C语言中结构体的内存对齐规则讲解

    目录 1.结构体的内存对齐规则 2.例子 3.为什么存在内存对齐 4.如何修改默认对齐数 1.结构体的内存对齐规则 1.第一个成员在与结构体变量偏移量为0的地址处. 2.其他成员变量都放在对齐数(成员的大小和默认对齐数的较小值)的整数倍的地址处. 对齐数=编译器默认的一个对齐数与该成员大小的较小值.(VS中默认的对齐数是8) 3.结构体总大小为最大对齐数(每个成员变量都有一个对齐数 )的整数倍. 4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最

  • go语言结构体指针操作示例详解

    目录 指针 go指针操作 不能操作不合法指向 new函数 指针做函数的参数 数组指针 结构体指针变量 结构体成员普通变量 结构体成员指针变量 结构体比较和赋值 结构体作为函数参数 指针 指针是代表某个内存地址的值.内存地址储存另一个变量的值. 指针(地址),一旦定义了不可改变,指针指向的值可以改变 go指针操作 1.默认值nil,没有NULL常量 2.操作符“&”取变量地址,“*“通过指针(地址)访问目标对象(指向值) 3.不支持指针运算,不支持“->”(箭头)运算符,直接用“.”访问目标成

随机推荐