c语言结构体的内存对齐

简介: c语言结构体的内存对齐

前言:

c语言中结构体的空间大小怎么算呢?就是把里面所有变量的字节大小全部加起来吗?

如果你这么想,说明你对结构体还不够了解,而要想知道结构体开辟空间的规则,那我们就必须先要知道内存对齐是什么。

接下来我就说说内存对齐是个啥玩意吧!

1.结构体内存对齐

举例:

struct s1 {
  char c1;
  int a;
  char c2;
}s1;
 
struct s2 {
  char c1;
  char c2;
  int a;
}s2;
int main() {
  printf("%d %d\n", sizeof s1, sizeof s2);
 
  return 0;
}

思考:为什么s1结构体和s2结构体只是声明的成员变量顺序不一样,但是求得的空间大小却有差异呢?

首先我们要明白结构体内存对齐的规则。

1.1结构体对齐规则:

1. 第一个成员在与结构体变量偏移量为0的地址处。

2. 其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。 对齐数 = 编译器默认的一个对齐数 与 该成员大小的较小值。 VS中默认的值为8

3. 结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

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

我们首先来观察结构体s1:

struct s1 {
  char c1;
  int a;
  char c2;
}s1;

根据对齐规则,我们第一个成员变量在与结构体变量偏移量为0的地址处,也就是在0这个位置开始。

注意,图中的编号仅代表地址的相对位置。

然后再看第二个成员变量 int a,a的字节大小为4,4比8小,所以a的对齐数是4,那么我们就在上一个成员变量的位置往下找到a对齐数整数倍的位置,于是我们把a放在4这个位置上。

再然后呢,根剧对齐规则,最后一个成员变量的相对位置放在8这个位置上(char c2的对齐数为1,8是1的整数倍数)。

所以最终结构体s1的成员变量存放的相对位置为:

那么这个时候的整个结构体的空间大小是什么呢?是不是最后一个成员变量的位置呢?

显然不是看最后一个成员变量所占最后一个字节的位置,因为这样一来sizeof (s1)=8,而这是错误的。

结构体总大小为最大对齐数(每个成员变量都有一个对齐数)的整数倍。

根据这一规则,我们先算出来结构体的最大对齐数,也就是a的对齐数,即4。

从第一个变量所占字节的第一个位置,到最后一个成员变量所占字节的最后一个位置,我们算出来至少需要9个字节才能把装下这三个成员变量(0-8),而大于或者等于9又是4的整数倍的第一个数是12,所以我们得到的结构体s1的空间大小是12个字节。同理观察结构体s2:

根据以上的对齐规则,我们很容易得到s2成员变量的内存结构位置,

算出来结构体的最大对齐数,也就是a的对齐数,即4。

从第一个变量所占字节的第一个位置,到最后一个成员变量所占字节的最后一个位置,我们算出来至少需要8个字节才能把装下这三个成员变量(0-7),而大于或者等于8又是4的整数倍的第一个数是8,所以我们得到的结构体s1的空间大小是8个字节。

根据内存规则我们很快就知道了为什么sizeof(s1)和sizeof(s2)的大小,那内存对齐的意义是什么呢?

1.2为什么会存在内存对齐?

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

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

2. 性能原因:

数据结构(尤其是栈)应该尽可能地在自然边界上对齐。 原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访 问。 

这里我们来举一个例子:

struct s1 {
  char c1;
  int a;
}s1;

如果不遵循内存对齐原则:

第一次读取我们可以顺利读取到变量c1.

第二次读取我们只能再找到整型a的前三个字节

第三次读取我们才能找到a的最后一个字节

所以,如果没有内存对齐,我们要想完整的找到这两个成员变量需要读取三次。

如果遵循内存对齐原则:

第一次读取我们就能找到变量c1.

而第二次读取我们就能找到变量a了

1.3总结:

结构体的内存对齐是拿空间来换取时间的做法。虽然根据内存对齐可能会开辟不必要的空间,但是为了保障读取数据的速度,拿空间换时间是值得的!

其实,在设计结构体的时候,我们的目的是既要满足对齐,又要节省空间,这又如何做到呢?

我们可以通过调整成员变量声明的顺序来减少结构体因为内存对齐的空间亏损。

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

S1和S2类型的成员一模一样,但是S1和S2所占空间的大小有了一些区别。

1.4 修改默认对齐数

vs的默认对齐数是8,那么我们可不可以修改默认对齐数呢?

之前我们见过了 #pragma 这个预处理指令,这里我们再次使用,可以改变我们的默认对齐数。\

#include<stdio.h>
#pragma pack(2)//修改默认对齐数为2
struct s1 {
  char c1;
  int a;
  char c2;
}s1;
#pragma pack()取消设置的默认对齐数,还原为默认
struct s2 {
  char c1;
  char c2;
  int a;
}s2;
int main() {
  printf("%d %d\n", sizeof s1, sizeof s2);
 
  return 0;
}

当我们修改默认对齐数,并且只影响结构体s1,那此时的sizeof(s1)又是多少呢?

答案是8,过程自己去算。

相关文章
|
5月前
|
存储 编译器 程序员
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
在C语言中,内存布局是程序运行时非常重要的概念。内存布局直接影响程序的性能、稳定性和安全性。理解C程序的内存布局,有助于编写更高效和可靠的代码。本文将详细介绍C程序的内存布局,包括代码段、数据段、堆、栈等部分,并提供相关的示例和应用。
134 5
【C语言】内存布局大揭秘 ! -《堆、栈和你从未听说过的内存角落》
|
5月前
|
存储 网络协议 编译器
【C语言】深入解析C语言结构体:定义、声明与高级应用实践
通过根据需求合理选择结构体定义和声明的放置位置,并灵活结合动态内存分配、内存优化和数据结构设计,可以显著提高代码的可维护性和运行效率。在实际开发中,建议遵循以下原则: - **模块化设计**:尽可能封装实现细节,减少模块间的耦合。 - **内存管理**:明确动态分配与释放的责任,防止资源泄漏。 - **优化顺序**:合理排列结构体成员以减少内存占用。
379 14
|
5月前
|
存储 编译器 C语言
【C语言】结构体详解 -《探索C语言的 “小宇宙” 》
结构体通过`struct`关键字定义。定义结构体时,需要指定结构体的名称以及结构体内部的成员变量。
299 10
|
5月前
|
存储 缓存 算法
【C语言】内存管理函数详细讲解
在C语言编程中,内存管理是至关重要的。动态内存分配函数允许程序在运行时请求和释放内存,这对于处理不确定大小的数据结构至关重要。以下是C语言内存管理函数的详细讲解,包括每个函数的功能、标准格式、示例代码、代码解释及其输出。
172 6
|
6月前
|
存储 C语言
C语言如何使用结构体和指针来操作动态分配的内存
在C语言中,通过定义结构体并使用指向该结构体的指针,可以对动态分配的内存进行操作。首先利用 `malloc` 或 `calloc` 分配内存,然后通过指针访问和修改结构体成员,最后用 `free` 释放内存,实现资源的有效管理。
535 13
|
6月前
|
存储 编译器 数据处理
C 语言结构体与位域:高效数据组织与内存优化
C语言中的结构体与位域是实现高效数据组织和内存优化的重要工具。结构体允许将不同类型的数据组合成一个整体,而位域则进一步允许对结构体成员的位进行精细控制,以节省内存空间。两者结合使用,可在嵌入式系统等资源受限环境中发挥巨大作用。
185 12
|
6月前
|
存储 数据建模 程序员
C 语言结构体 —— 数据封装的利器
C语言结构体是一种用户自定义的数据类型,用于将不同类型的数据组合在一起,形成一个整体。它支持数据封装,便于管理和传递复杂数据,是程序设计中的重要工具。
|
6月前
|
传感器 人工智能 物联网
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发
C 语言在计算机科学中尤其在硬件交互方面占据重要地位。本文探讨了 C 语言与硬件交互的主要方法,包括直接访问硬件寄存器、中断处理、I/O 端口操作、内存映射 I/O 和设备驱动程序开发,以及面临的挑战和未来趋势,旨在帮助读者深入了解并掌握这些关键技术。
146 6
|
6月前
|
大数据 C语言
C 语言动态内存分配 —— 灵活掌控内存资源
C语言动态内存分配使程序在运行时灵活管理内存资源,通过malloc、calloc、realloc和free等函数实现内存的申请与释放,提高内存使用效率,适应不同应用场景需求。
|
6月前
|
存储 算法 程序员
C 语言指针详解 —— 内存操控的魔法棒
《C 语言指针详解》深入浅出地讲解了指针的概念、使用方法及其在内存操作中的重要作用,被誉为程序员手中的“内存操控魔法棒”。本书适合C语言初学者及希望深化理解指针机制的开发者阅读。

热门文章

最新文章