内存之对齐,一文即可知
😊点此到文末惊喜↩︎
内存对齐
1.概述
- 定义(什么是内存对齐)
- 编译器为程序中的每个
数据单元
安排在适当的位置上,从而减少CPU访存次数
,提高数据读取效率的一种方式。
- 原因(为什么要内存对齐):
- 平台移植:不同硬件平台不一定支持
内存的随机存取
,使用内存对齐可以保证每次访问都从块地址头部
开始存取 - 性能原因:为了访问未对齐的内存数据,处理器需要至少进行两次内存访问;而对齐的内存访问仅需要一次访问。
- 如何进行内存读取
- 32位系统采用的是4字节对齐方式,即从4字节的倍数地址开始读取数据,这样可以保证数据对齐,提高内存读取的效率。而基本数据类型的大小设置,保证了数据对齐后不会出现跨4字节的两次读取问题。
- 缺点:内存对齐没有充分利用存储空间,但是
以空间换时间
对于常见业务场景会更高效
struct CacheTest{
int a;
char b[64];
int b;
};
- 内存对齐示例
#include<stdio.h>
struct Test {
char y;
int x;
};
int main() {
printf("%d\n",sizeof(Test));
return 0;
}
2.如何进行内存对齐
- 默认对齐系数:在每个特定平台的编译器可能不同,gcc中默认#pragma pack(4)
- 使用伪指令#pragma pack(n),C编译器将按照n个字节对齐。
- 使用伪指令#pragma pack(),取消编译的对齐优化
- __attribute(aligned (n)),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。
- attribute(packed),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。
- 基本类型的对齐值就是其sizeof值。常见数据类型**
数据类型 | char | short | int | float | long | double | longlong |
---|
大小 | 1Byte | 2Byte | 4Byte | 4Byte | 4Byte | 8Byte | 8Byte |
- 类、结构体和union的数据成员对齐规则**
- 成员间填充:结构体每个成员相对于结构体首地址的偏移量(offset)都是
成员自身大小的整数倍
- 成员尾填充:结构体的总大小为结构体
最大基本类型成员大小的整数倍
- 按照最大类型size划分格子,依次填入,如果跨越格子则从第二个开始
- 类、结构体和union的整体对齐**
- 结构体变量的首地址,必须是结构体变量中的
最大基本数据类型成员所占字节数
的整数倍
#include<stdio.h>
struct A{
char a;
char b;
char c;
short d;
};
struct {
int i;
char c1;
char c2;
}Test1;
struct{
char c1;
int i;
char c2;
}Test2;
struct{
char c1;
char c2;
int i;
}Test3;
int main() {
printf("%d\n",sizeof(Test1));
printf("%d\n",sizeof(Test2));
printf("%d\n",sizeof(Test3));
return 0;
}
3.位域对齐
- 定义:把一个字节中的“位”按照实际的需求分成不同的区域,表明每个区域位数、区域的域名,并允许程序通过照域名进行操作
- 结构
struct 位域的结构体名 {
【类型说明符】 【位域名】:【位域的长度】;
}
- 规则
- 位域
不允许跨两个字节
,因此位域的长度不能大于一个字节的长度,也就是不能超过8位二进位。 - 位域
可以无位域名
,此时只用于填充或调整位置,但无法使用。 - 各个成员
按声明顺序
在内存中顺序存储
,第一个成员的地址和整个结构的地址相同,向结构体成员中sizeof的最大的成员对齐。
大端存储与小端存储
1.基本概念
- 定义
- 分类
- 小端存储:数据的高字节存储在高地址中,数据的低字节存储在低地址中
- 大端存储:数据的高字节存储在低地址中,数据的低字节存储在高地址中
- 判断
int a = 1;
int a = 1;
#include<stdio.h>
int main()
{
int a = 1;
char* p = &a;
if (*p){
printf("小端存储\n");
}else{
printf("大端存储\n");
}
return 0;
}
- 适用场景:TCP/IP协议中
网络字节序是按大端存储
的,多数操作系统都是小端存储
,所以对数据包的封装和处理需要转序 - 字节序转换函数
#include <stdio.h>
#include <stdint.h>
uint16_t swap_endian_16(uint16_t x) {
return (x << 8) | (x >> 8);
}
uint32_t swap_endian_32(uint32_t x) {
return (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24);
}
uint64_t swap_endian_64(uint64_t x) {
return ((uint64_t)swap_endian_32(x & 0xffffffffull) << 32) | swap_endian_32(x >> 32);
}
int main() {
uint16_t x16 = 0x1234;
uint32_t x32 = 0x12345678;
uint64_t x64 = 0x123456789abcdef0;
uint16_t y16 = swap_endian_16(x16);
uint32_t y32 = swap_endian_32(x32);
uint64_t y64 = swap_endian_64(x64);
printf("Original uint16_t: 0x%x, swapped uint16_t: 0x%x\n", x16, y16);
printf("Original uint32_t: 0x%x, swapped uint32_t: 0x%x\n", x32, y32);
printf("Original uint64_t: 0x%llx, swapped uint64_t: 0x%llx\n", x64, y64);
return 0;
}
少年,我观你骨骼清奇,颖悟绝伦,必成人中龙凤。
秘籍(点击图中书籍)·有缘·赠予你
🚩点此跳转到首行↩︎
参考博客
- 内存对齐的规则以及作用
- 采用内存对齐的方式存储数据的意义
- 一文轻松理解内存对齐
- C++ 位域
- C++ 位域
- C++ 位域
- C++ 位域
- C++ 位域