内存对齐的规则
内存对齐的规则有3条
struct 或 union (以下统称结构体)的数据成员:
第一个数据成员A放在偏移为 0 的地方。
然后循环下面的步骤:
- 假设 对齐系数为D = #pragma pack(指定的数n),在 GCC 64位下默认是8
- 假设 当前数据成员是 B,B 的自身长度是 b
- 假设 目前结构体的长度,也就是 B 的原始偏移为 Offset
- 检查 Offset 是否是 min(D, b) 的整数倍
- 如果是,B 的偏移就是 Offset
- 如果不是,就填充0,直至 Offset 是 min(D, b) 的整数倍
数据成员为结构体
如果结构体的数据成员还为结构体,则该数据成员的“自身长度”为其内部最大元素的大小。(struct a 里存有 struct b,b 里有char,int,double等元素,那 b “自身长度”为 8)
结构体的整体对齐规则
在数据成员按照上述第一步完成各自对齐之后,结构体本身也要进行对齐。对齐会将结构体的大小调整为(#pragma pack(指定的数n) 与 结构体中的最大长度的数据成员中较小那个的整数倍,不够的补齐。
内存对齐的目的
一般的解释是这样的:‘ 截屏2020-06-29 下午4.40.50.png
对此解释,我的疑惑如下:
- 为什么不可以从地址1开始读,读取4个字节,为什么要先读取3个,再读取1个
- 如果说是硬件实现上的原因导致必须要按照对齐系数读取块,那例子中起始地址假设成了0,如果起始地址是2呢?因为如果一个结构体A中只有两个char类型的数据成员,那它的长度就是2,对齐后也是2。假设例子中的结构体是B,如果地址0放了个A,地址2放了个B,B的对齐系数是4,那不就没有对齐了?
内存对齐对CPU效率的影响
还看到一种说法,如果对齐系数设置得偏大,内存浪费较多(这个好理解),如果对齐系数设置得偏小,会导致CPU需要访问内存的次数比理想的偏多。
比如:一个int类型是4个字节,如果对齐系数是4,就只需要访问内存1次,如果对齐系数是1,就需要访问内存4次,可是为什么不能用 起始地址+长度 的方式访问1次内存呢?
我觉得我这种想法好像又绕回到了
为什么不可以从地址1开始读,读取4个字节,为什么要先读取3个,再读取1个 这里
c语言中内存对齐的实现
c 语言中是可以指定对齐系数的,据说是通过 __attribute__ ((aligned (n))) 的方式。
struct A {
int a;
long b;
} __attribute__ ((aligned (1)));
但是我在在线 c 语言编译器和 MAC 下的 gcc 编译器上都看不出效果。
但是在iOS的OC文件中可以通过 #pragma pack(n) 导致当前文件中的结构体对齐系数被改变,对其他文件无效
那么问题就来了:
- 应该C语言中确实是可以指定对齐系数的,但可能我没有找到正确的方式,或者指定对齐系数这种操纵已经被废弃了,但应该至少之前是可以的。所以,c语言编译器中是怎么实现指定对齐系数的?对齐系数总得有地方存储吧?
- 如果对齐系数是对整个程序有效的,那对齐系数倒是好存了,但是无从考证了。。。
- 如果对齐系数只是对某个结构体有效,那对齐系数存在哪里了?c语言的结构体没有什么猫腻,有什么没什么使用者也能看到的,从没见过结构体中有存对齐系数的地方
- 通过其他文章中说的,对齐系数可以是1, 2, 4, 8, 16....,可以推测对齐系数应该是可以变化的,那是在一个进程中可以变化?还是每一次访问都可以不同?
如果知道对齐系数是存在哪里的,这些问题应该都会迎刃而解。
OC中内存对齐
如果定义这样一个类
@interface Student : NSObject {
int b;
int a;
char c;
}
通过命令 xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc ViewController.m -o object.cpp 转成c++源码,结果如下:(Student定义在ViewController中)
static struct /*_ivar_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count;
struct _ivar_t ivar_list[3];
} _OBJC_$_INSTANCE_VARIABLES_Student __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_ivar_t),
3,
{{(unsigned long int *)&OBJC_IVAR_$_Student$b, "b", "i", 2, 4},
{(unsigned long int *)&OBJC_IVAR_$_Student$a, "a", "i", 2, 4},
{(unsigned long int *)&OBJC_IVAR_$_Student$c, "c", "c", 0, 1}}
};
这个结构体中最后一个数据成员是_ivar_t的数组,_ivar_t定义如下:
struct _ivar_t {
unsigned long int *offset; // pointer to ivar offset location
const char *name;
const char *type;
unsigned int alignment;
unsigned int size;
};
也就是说:
int 类型的 alignment 是2,size 是4
char 类型的 alignment 是0,size 是1
what❓❓❓
完全黑人问号