对齐规则只有1,2,4,8,16, 如果面试写了3,5,6,7等等都是错误的,至少我的编译器没通过。
用C++写的代码如下,因为里面写了注释,所以就不赘述了:
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <string>
#include <string.h>
#include <memory>
#include <typeinfo>
#include <algorithm>
#define N 4
#pragma pack(4)
static int nextAddr = 0;
static int maxPack = 0;
using AlignMem = struct AlignMem {
char ch1[1];
short s;
char ch;
double d;
char ch2[9];
int i;
};
bool isCharType(decltype(typeid(char)) Type) {
return (NULL != strstr(Type.name(), "_c") || typeid(char) == Type);
}
/* 算法參照規則:
* 1. char類型的直接計算地址,不用考慮對齊情況
* 2. 非char類型的,根據其存放的開始地址計算,取自身類型和pack對齊規則中較小值,
* 如果存放開始地址可以整除較小值,則無需補字節; 否則補齊字節,直到可以整除爲止.
* 3. 結構體整體對齊計算,結構體中非char成員類型最大值與pack對齊規則要求值兩者中較小值,
* 用最後算出的地址整除該值,如果整除則無需補齊字節,否則補齊字節,直到可以整除爲止.
* 例如計算AlignMem:
* 存放s後, 則short存放地址爲1, sizeof(short) < 4(pack裏面的對齊值),所以應該按照2對齊,
* 因此1補齊1個字節後等於2,那麼short存放地址就是2開始,下一個ch存放地址就是2 + sizeof(short)=4,
* 按照上述方式依次計算,最終應該是這樣:
* (0+sizeof(ch1) + 1)
* (sizeof(s))
* (sizeof(ch) + 3)
* (sizeof(d))
* (sizeof(ch2) + 3)
* (sizeof(i))
* 上面算完正好是32, 结构体最大类型是double=8, 对齐规则是4字节对齐,其它俩中最小值进行结构体整体对齐,
* 也就是用 32 % 4 = 0,所以不用补齐,但是如果计算出来的是33,那么就要补齐了,应该补齐到36.
* */template <typename T, typename TI>
void calculateMem(T& typeID, TI typeLength) {
if (typeLength <= 0 && 0 == nextAddr) {
return;
}
int diff = 0;
int n = 0;
/* N - pack()要求對齊的值
* typeLength - 當前類型的大小
* maxPack - 找出並保存結構體成員類型(非char)佔用最大的值
* */
if(!isCharType(typeID))
maxPack = maxPack > typeLength ? maxPack : typeLength;
// n 爲對齊規則,當自身對齊時, n應該使用
// pack() 與 自身類型較小的值.
if (typeLength > 0)
n = N > typeLength ? typeLength : N;
// n 爲對齊規則,當整體對齊時, n應該使用
// pack() 與結構體中類型最大的那個較小的值.
else
n = maxPack > N ? N : maxPack;
// 當typeLength小於等於0時,認爲是沒有要計算佔用空間的類型,即認爲結構體結束
if(typeLength <= 0) {
// nextAddr % n 不爲0表示需要整體補齊
if(nextAddr % n) {
diff = n - (nextAddr % n);
nextAddr = nextAddr + diff;
}
return ;
}
/* 上面做了兩件事:
* 1. 對齊的值n的計算
* 2. 結構體結束(typeLength <= 0)時,整體補齊
*
* 下面做了兩件事:
* 1. 處理不需要對齊的情況
* 2. 處理需要自身對齊的情況
* */
if(0 == nextAddr
|| ((nextAddr % n + typeLength) <= n )) {
nextAddr = nextAddr + typeLength;
return ;
}
if(!isCharType(typeID) && nextAddr % n) {
diff = n - (nextAddr % n);
}
nextAddr = nextAddr + diff + typeLength;
}
int main() {
// 測試部分
AlignMem alignmem;
printf("sizeof AlignMem = %lu\n",sizeof(alignmem));
calculateMem<decltype(typeid(alignmem.ch1)), decltype(sizeof(alignmem.ch1))>(typeid(alignmem.ch1), sizeof(alignmem.ch1));
calculateMem<decltype(typeid(alignmem.s)), decltype(sizeof(alignmem.s))>(typeid(alignmem.s), sizeof(alignmem.s));
calculateMem<decltype(typeid(alignmem.ch)), decltype(sizeof(alignmem.ch))>(typeid(alignmem.ch), sizeof(alignmem.ch));
calculateMem<decltype(typeid(alignmem.d)), decltype(sizeof(alignmem.d))>(typeid(alignmem.d), sizeof(alignmem.d));
calculateMem<decltype(typeid(alignmem.ch2)), decltype(sizeof(alignmem.ch2))>(typeid(alignmem.ch2), sizeof(alignmem.ch2));
calculateMem<decltype(typeid(alignmem.i)), decltype(sizeof(alignmem.i))>(typeid(alignmem.i), sizeof(alignmem.i));
// 這裏要傳0l下去,因爲我是按照0L表示結構體結束,不然的話會缺少整體對齊
calculateMem<decltype(typeid(int)), decltype(sizeof(int))>(typeid(int), 0L);
printf("calculateMem alignmem = %d\n",nextAddr);
nextAddr = 0;
return EXIT_SUCCESS;
}