一 思考
struct Person{
int a;
char b;
double c;
} person;
struct People{
char b;
double c;
int a;
}people;
int main ()
{
printf ("%d\n",sizeof(people);
printf ("%d\n",sizeof(person);
return 0;
}
真機(jī)運(yùn)行,64位系統(tǒng)上 char + int + double = 1 + 4 + 8
應(yīng)該占12個字節(jié) ,但運(yùn)行打印后發(fā)現(xiàn)為24
,16
,說明系統(tǒng)存儲相應(yīng)類型有其固定方式即為內(nèi)存對齊
一 為什么要內(nèi)存對齊
為了提高程序的性能磕蒲,數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在自然邊界上對齊,為了訪問未對齊的內(nèi)存履磨,處理器需要作兩次內(nèi)存訪問
真椿;然而,對齊的內(nèi)存訪問僅需要一次訪問. 例如2-5 4字節(jié)數(shù)據(jù),在沒有內(nèi)存對齊的情況下要取兩次 0-3 4-7 才能得到2-5所需數(shù)據(jù).
二 規(guī)則 - 如何計算
- 假設(shè)
m
為當(dāng)前成員起始存儲位置n
為其所需存儲大小 需滿足m % n == 0
否則需從m+1
位開始 直到滿足條件 - 當(dāng)結(jié)構(gòu)體嵌套了結(jié)構(gòu)體時,以數(shù)據(jù)成員的結(jié)構(gòu)體的自身長度作為外部結(jié)構(gòu)體的最大成員的內(nèi)存大小蚂会,比如結(jié)構(gòu)體a嵌套結(jié)構(gòu)體b,b中有
char
、int
各吨、double
等,則b的自身長度為8開始計算b結(jié)構(gòu)體內(nèi)子成員位置 - 最后結(jié)構(gòu)體的內(nèi)存大小必須是結(jié)構(gòu)體中最大成員內(nèi)存大小的整數(shù)倍袁铐,不足的需要補(bǔ)齊
解釋下上面的24 16 是如何得到的:
Person 結(jié)構(gòu)結(jié)構(gòu)體
-
int a
占4個字節(jié)從頭開始 -
chat b
一個字節(jié) 占據(jù) 4號位 -
double 8
個字節(jié)5, 6 , 7
, 無法整除8 故偏移到8
號位置 開始到15
號 共占8位 - 結(jié)構(gòu)體總大小位置為
16
最大成員為double c
8位 可被16整除
故返回16個字節(jié)
People 結(jié)構(gòu)結(jié)構(gòu)體
-
chat b
從0開始 占一個字節(jié) -
double c
8個字節(jié)偏移到8號位置向后8位到15
-int a
4字節(jié)可以被16整除 從16開始向后4位到19 - 最大成員為
double c
8位, 向后補(bǔ)齊到23 ,共24位可整除8,返回24個字節(jié)
三 結(jié)構(gòu)體嵌套結(jié)構(gòu)體
struct Teacher{
char d;
int e;
short f;
int g;
}teacher;
struct tClass{
char a;
int b;
short c;
struct Teacher teacher1;
}class;
int main ()
{
printf ("%d\n",sizeof(class); //28
return 0;
}
-
chat a , int b , short c
如上面結(jié)構(gòu)體一樣計算到位置9 -
Class
中最大成員為int 4字節(jié) 所以偏移到12號位置存儲class
中的chat d
- 剩下的類似普通結(jié)構(gòu)體直到27號位置存儲完共28位 28 可以整除最大成員大小
int 4字節(jié)
返回28
三 拓展, 應(yīng)用
-
pragma pack(n) & attribute((aligned (n)))
#pragma pack(1)
struct Person{
int a;
char b;
double c;
} person;// sizeof(person) = 13
#pragma pack()
#define PACKED __attribute__((packed))
struct PACKED People{
char b;
double c;
int a;
}people;//sizeof(people) = 13
-
#pragma pack(n)
為設(shè)置采用多少字節(jié)對齊 -
#define PACKED __attribute__((packed))
取消編譯過程中的優(yōu)化對齊
四 OC中的內(nèi)存優(yōu)化
- 由 Person 與People 我們可以得知,在啟用內(nèi)存對齊后,成員相同的結(jié)構(gòu)體,成員順序不同,則最后實際開辟內(nèi)存大小也不同,并且所占內(nèi)存大小大的成員在結(jié)構(gòu)體位置靠前時,最終開辟的空間可能會更小. 不過OC中采用的是16字節(jié)對齊,這種方式優(yōu)化沒意義.
OC 屬性重排驗證
@interface Person : NSObject
@property(nonatomic,assign)NSInteger age;
@property(nonatomic,assign)BOOL sex;
@property(nonatomic,copy)NSString *name;
@property(nonatomic,copy)NSString *address;
@end
-聲明一個對象Person 其屬性順序如上
- 賦值
Person *person = [[Person alloc] init];
person.name = @"范熱熱";
person.address = @"li bed";
person.age = 100;
person.height = 400;
person.a = 'f';
person.b = 'b';
NSLog(@"objc對象類型占用的內(nèi)存大薪已选:%lu",sizeof(person));
NSLog(@"objc對象實際占用的內(nèi)存大泻峄搿:%lu",class_getInstanceSize([Person class]));
NSLog(@"objc對象分配的內(nèi)存大小:%lu",malloc_size((__bridge const void*)(person)));
查看打印結(jié)果 為 8 40 48
-
sizeof(person)
得到的是person指針的大小 占8位 沒毛病 -
malloc_size((__bridge const void*)(person))
實際開辟的大小,采用16字節(jié)對齊要被16整除 得48 也可以 -
class_getInstanceSize([Person class]) 這個40是如何得到呢 答案是蘋果實際采用的是8字節(jié)對齊 驗證一下
- 首先我們發(fā)現(xiàn) 實際內(nèi)存中存儲的屬性值與我們在.h中定義的順序并不一樣
- 第一個占8位的為ISA指針地址
- 第二個8位 一共包含了
int age
chat a
chat b
三個屬性 - 第三個8位 為
int height
- 第四個第五個8位為 NSString 類型