什么是大小端?
大端模式,是指數(shù)據(jù)的高字節(jié)保存在內(nèi)存的低地址中,而數(shù)據(jù)的低字節(jié)保存在內(nèi)存的高地址中,這樣的存儲模式有點兒類似于把數(shù)據(jù)當作字符串順序處理:地址由小向大增加,而數(shù)據(jù)從高位往低位放;這和我們的閱讀習慣一致。
小端模式,是指數(shù)據(jù)的高字節(jié)保存在內(nèi)存的高地址中,而數(shù)據(jù)的低字節(jié)保存在內(nèi)存的低地址中,這種存儲模式將地址的高低和數(shù)據(jù)位權(quán)有效地結(jié)合起來,高地址部分權(quán)值高,低地址部分權(quán)值低。
要注意一點,也就是,這里的高字節(jié)和低字節(jié)與高地址和低地址是兩個截然不同的概念。習慣上,將一個數(shù)字按位寫到紙上時,我們會從左至右,從高位到低位一位一位地寫,這是一種具體的行為。在談到一個多字節(jié)整型數(shù)據(jù)的高低字節(jié)時,我們并沒有考慮它如何進行存儲,而是按照人類的習慣,將權(quán)值高的字節(jié)稱作高字節(jié),權(quán)值地的稱作低字節(jié)。
大小端模式的判斷
Linux 源碼中判斷大小端模式的方法
static union {
int i;
char c[4];
} endian_test = {{'l', '?', '?', 'b'}};
#define ENDIANNESS ((char)endian_test.i)
理解上面的代碼的關(guān)鍵是,
int -> char 的轉(zhuǎn)換要截斷到int的最后一個字節(jié)(只保留低8位)。
字符串不用考慮大小端模式的問題,從左至右內(nèi)存地址依次增高
+---+---+---+---+
| l | ? | ? | b +
+---+---+---+---+
如果是小端模式,那么低字節(jié)保留在低地址,而這里如果按
char來尋址,低地址也即是最左邊的那個字節(jié),其中存儲了'l';如果是大端模式,那么低字節(jié)保留在高地址,而這里如果按
char來尋址,高地址也即是最右邊的那個字節(jié),其中存儲了'b'。
什么時候需要判斷大小端模式?
一般情況下,只有需要跟硬件直接打交道的地方才需要自己去判斷大小端,其余的地方編譯器和操作系統(tǒng)會幫你搞定,比如對多字節(jié)數(shù)據(jù)類型進行按位運算。另外,網(wǎng)絡傳輸數(shù)據(jù)的時候是按照字節(jié)傳輸?shù)?,這時候就需要考慮字節(jié)序。
一個簡單的例子
#include <stdio.h>
#include <stdlib.h>
static union {
int i;
char c[4];
} endian_test = {{'l', '?', '?', 'b'}};
#define ENDIANNESS ((char)endian_test.i)
void printbyte(char byte)
{
for (int i = 0; i < 8; ++i)
putchar('0' + ((byte >> (7 - i)) & 0x1));
}
// 從高字節(jié)到低字節(jié),高位到低位
void print_int4b(int x)
{
const char *s = (char *)&x;
if (ENDIANNESS == 'l') {
for (int i = 3; i >= 0; --i)
printbyte(s[i]);
}
else if (ENDIANNESS == 'b') {
for (int i = 0; i <= 3; ++i)
printbyte(s[i]);
}
else {
fprintf(stderr, "%s\n", "unknown endianness");
exit(1);
}
putchar('\n');
}
void print_int32(int x)
{
for (int i = 0; i < 32; ++i)
putchar('0' + ((x >> (31 - i)) & 0x1));
putchar('\n');
}
int main()
{
int i = 100;
print_int4b(i);
print_int32(i);
return 0;
}