首页 > 系统相关 >C语言 ——— 结构体内存对齐

C语言 ——— 结构体内存对齐

时间:2024-08-15 21:26:54浏览次数:15  
标签:字节 成员 偏移量 C语言 char 对齐 体内 结构

目录

发现问题

 偏移量宏:offsetof()

结构体内存的对齐规则

小结 


发现问题

有以下两个结构体:

结构体1:

struct S1
{
	char c1; // 1字节
	int i; // 4字节
	char c2; // 1字节
};

结构体2:

struct S2
{
	char c1; // 1字节
	char c2; // 1字节
	int i; // 4字节
};

通常情况下:结构体1的大小是6字节(char是1字节 + int是4字节 + char是1字节);结构体2的大小是6字节(char是1字节 + char是1字节 + int是4字节)

打印结构验真假:

打印发现 结构体1 和 结构体2 所占内存空间的大小并不一样,出现这种结果的原因是因为结构体内存对齐规则的原因,利用宏 offsetof() 可以帮助判断


 偏移量宏:offsetof()

offsetof()  这个宏可以计算结构体成员相较于结构体起始位置的偏移量

头文件为:#include<stddef.h>

计算结构体所有成员对于结构体起始位置的偏移量: 

struct S1
{
	char c1;
	int i;
	char c2;
};
int main()
{
	printf("%d", offsetof(struct S1, c1));
	printf("%d", offsetof(struct S1, i));
	printf("%d", offsetof(struct S1, c2));
	return 0;
}

计算结果:

可以发现,c1 是char类型的变量,所占内存空间只有1个字节,且是第一个变量,所以放在结构体起始位置偏移量为0的地方,那为什么第二个变量要放在离起始位置偏移量4的地方,中间的3个字节为什么要浪费掉?

由以上问题分析得出,结构体成员不是按照顺序在内存中连续存放的,是有一定的对齐规则


结构体内存的对齐规则

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

2. 从第二个成员开始,往后的每个成员都要对齐到某个对齐数的整数倍处

3. 结构体的总大小,必须是最大对齐数的整数倍,而这最大对齐数也就是结构体所有成员的对齐数中最大的值

对齐数:结构体成员自身的大小和默认对齐数的较小值

默认对齐数:VS编译器上的默认对齐数是8,而gcc这种编译器没有默认对齐数,对齐数就是结构体成员的自身大小 

由以上的规则,我们可以得知,为什么第二个变量要放在相较于结构体起始位置的偏移量为4的位置了

再次复盘以上结构体:

struct S1
{
	char c1;
	int i;
	char c2;
};

第一个成员:char类型的c1变量,放在相较于结构体起始位置的偏移量为0的位置

第二个成员:int类型的i变量,i的大小是4个字节,VS中默认对齐数是8,在4和8中取较小值,也就是4,所以第二个成员变量 i 要放在偏移量为4的倍数处,所以成员变量 i 的偏移量是 4

第三个成员:char类型的c2变量,c2 的大小是1个字节,在1和8中取较小值,也就是1,所以第三个成员变量 c2 要存放在偏移量为1的倍数处,且偏移量为4、5、6、7的位置已经被第二个成员所占,所以 c2 存放在偏移量为8的位置

结构体总大小: 

printf("%d\n", sizeof(struct S1));

结构体的总大小是最大对齐数的整数倍,由以上第二个成员可得出,最大对齐数是4,所以结构体的总大小必须是4的倍数,而当前结构体的大小是9,因为第三个成员是放在偏移量为8的位置的,且占一个字节,所以一共占了9个字节,9不是4的倍数,所以最4的最小整数倍是12,所以最后整个结构体的总大小是12个字节


小结 

除了结构体的第一个成员是放在结构体的起始处位置(也就是相较于结构体起始位置偏移量为0的位置),其他的成员要按照对齐规则存放

且结构体的总大小是结构体成员最大对齐数的整数倍

标签:字节,成员,偏移量,C语言,char,对齐,体内,结构
From: https://blog.csdn.net/weixin_55341642/article/details/141229948

相关文章

  • 字符串函数!!!(续)(C语言)
    一.strtok函数的使用继续上次的学习,今天我们来认识一个新的函数strtok,它的原型是char*strtok(char*str,constchar*sep),sep参数指向了一个字符串,定义了用作分隔符的字符合集,第一个参数指定⼀个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。st......
  • 嵌入式初学-C语言-二四
    Void与void*的区别定义:Void:空类型,是数据类型的一种Void*:是指针类型,是指针类型的一种,可以匹配任何类型的指针,类似于通配符Void说明:void作为返回值类型使用,表示没有返回值,作为参数,表示形参列表为空,在调用函数时不能给实参//函数声明voidfun(void);//等效于voidfun()......
  • C语言学习笔记 Day13(复合类型/自定义类型)
    Day13 内容梳理:目录Chapter9 复合类型(自定义类型)9.1结构体(1)结构体变量定义、初始化(2)嵌套结构体(3)结构体赋值(4)结构体和指针(5)结构体做函数参数9.2共用体(联合体)9.3枚举9.4typedef关键字Chapter9 复合类型(自定义类型)9.1结构体有时需要将不同类型的数组......
  • C语言无脑小游戏三子棋程序
    #include<stdio.h>#include<time.h>#include<stdlib.h>#include<windows.h>#defineROW3#defineCOL3voidmenu();//菜单函数声明voidgame();//游戏函数声明voidInit_board(charboard[ROW][COL],introw,intcol);//棋盘初始化函数声明voidDisplay_board......
  • C语言最后一讲——预处理超详解
    文章目录1.预定义符号2.`#define`定义常量3.`#define`定义宏4.带有副作用的宏参数5.宏替换的规则6.宏函数的对比7.#和##7.1#运算符7.2##运算符8.命名约定9.`#undef`10.命令行定义11.条件编译12.头文件的包含12.1头文件被包含的方式:12.1.1本地文......
  • C语言中的操作符:深入解析与应用
    引言C语言提供了丰富的操作符,用于执行算术运算、逻辑判断、位操作等。这些操作符是编程语言中的基础构件,它们使得程序能够进行复杂的数据处理和逻辑控制。本文将详细介绍C语言中的各种操作符,包括它们的类型、用法和一些实际应用示例。操作符的分类算术操作符算术操作符用于......
  • HexView 刷写文件脚本处理工具-命令行介绍(一)-数据对齐(/Adxx或/AD:yy)
    数据对齐(/Adxx或/AD:yy)每个块的起始地址将被对齐到给定参数xx的倍数。如果省略分隔符‘:’或‘=’,则参数xx被解释为十六进制值。如果使用了分隔符,则值xx以C风格进行解释,例如/AD:0xFF与/AD:255或/AD:11111111b相同。这个值只能是无符号字符值。示例说明......
  • HexView 刷写文件脚本处理工具-命令行介绍(二)-对齐长度(/AL[:length])
    对齐长度(/AL[:length])这个选项与/AD参数结合使用非常有用。它也将所有块的长度对齐,使其成为/Adxx选项中给定参数的倍数。示例说明:/AD4/AL如果有一个地址范围从0xE432到0xE47E的块,它将被对齐到0xE430到0xE47F。所有的字符将被填充为0xFF,或者被/Afxx指......
  • C语言内存管理,分配、使用、释放以及安全性
    在C++中,内存分配是通过几种不同的方式来管理的。这包括自动存储、静态存储和动态存储。下面分别解释这些存储类别以及如何使用它们进行内存分配。#1,自动存储(AutomaticStorage)这是最常用的存储类型,当一个变量在函数内被声明时,它会自动获得存储空间,并且在函数结束时自动释放。例......
  • C语言指针详解-上
    C语言指针详解-上前言1.指针的基本概念1.1指针是什么1.2指针的声明与初始化1.3取地址符`&`和解引用符`*``&`运算符用于**获取变量的地址**`*`运算符用于访问指针指向的值2.指针的类型常见数据类型的指针指针与数组、字符串数组指针结构体指针函数指针二级指针void指......