目錄
前言
一、整形數據在內存中的存儲
二、大小端字節序
三、大小端字節序的判斷
四、字符型數據在內存中的存儲
總結
前言
? ? ?本文主要講述整型包括字符型是如何在內存中存儲的,涉及到大小端字節序這一概念,還有如何判斷大小端,希望對大家有所幫助
??感謝支持,點贊關注不迷路??
(本文內容涉及到整形提升,如不了解,主頁中可查看詳細,兩篇結合起來看更深入)
一、整形數據在內存中的存儲
我們都知道。整數的2進制表示有3種,即原碼、反碼、補碼。
? ? ?
有符號的整數,三種表示方法均有符號位和數值位兩部分,符號位都是用0表示“正”,用1表 示“負”,最高位的一位是被當做符號位,剩余的都是數值位。
- 原碼:直接將數值按照正負數的形式翻譯成二進制得到的就是原碼。
- 反碼:將原碼的符號位不變,其他位依次按位取反就可以得到反碼。
- 補碼:反碼+1就得到補碼。
正整數的原、反、補碼都相同。
負整數的三種表示方法各不相同。
對于整形來說:數據存放內存中其實存放的是補碼。
為什么呢?
? ? ?
在計算機系統中,數值?律用補碼來表示和存儲。 原因在于,使用補碼,可以將符號位和數值域統一處理; 同時,加法和減法也可以統一處理(CPU只有加法器)此外,補碼與原碼相互轉換,其運算過程是相同的(都是取反加1),不需要額外的硬件電路。
(主頁·位操作符有詳細舉例)
二、大小端字節序
運行以下代碼,在vs(32位)調試窗口觀察其在內存中的存儲情況
#include <stdio.h>int main()
{int a = 0x11223344;return 0;
}
內存調試窗口:
(0x0058FB3C是a在內存中的首地址)
發現:a = 0x11223344,它在內存中存儲的卻是 44 33 22 11,是倒著存儲的。相信我們平時調試的時候肯定會有這樣的疑問,這就涉及到了大小端字節序了。
什么是大小端?
其實超過一個字節的數據在內存中存儲的時候,就有存儲順序的問題,按照不同的存儲順序,我們分為大端字節序存儲和小端字節序存儲,下面是具體的概念:
- 大端(存儲)模式: 是指數據的低位字節內容保存在內存的高地址處,而數據的高位字節內容,保存在內存的低地址處。
- 小端(存儲)模式: 是指數據的低位字節內容保存在內存的低地址處,而數據的高位字節內容,保存在內存的高地址處。
解釋說明:例如上面的 a = 0x11223344,我們按照數學方式讀這個數時,44是不是個位和十位。它相比于前面的 112233?是不是算低位。44?就是一個低位字節內容,VS是以小端模式存儲數據的,所以 44 就存儲在內存的低地址處,其余的就按順序往高處存儲,這樣我們看到的就是 44 33 22 11了。那么假如我們用的是大端模式存儲數據的,那么我們看到的就是 11 22 33 44 ,11在內存中對應的是低地址,44 是高地址。
為什么有大小端?
為什么會有大小端模式之分呢?
? ??
這是因為在計算機系統中,我們是以字節為單位的,每個地址單元都對應著?個字節,?個字節為8 bit 位,但是在C語言中除了8bit的 char 之外,還有16bit的 short 型,32bit的 long 型(要看具體的編譯器),另外,對于位數大于8位的處理器,例如16位或者32位的處理器,由于寄存器寬度大于一個字節,那么必然存在著一個如何將多個字節安排的問題。因此就導致了大端存儲模式和小端存儲模式。
? ? ?
例如:?個 16bit 的 short 型 x ,在內存中的地址為 0x0010,x 的值為 0x1122 ,那么0x11為高字節,0x22為低字節。對于大端模式,就將0x11放在低地址處,即0x0010中,0x22放在高地址處,即0x0011中。小端模式,剛好相反。我們常用的X86 結構是小端模式,而?KEIL C51 則為大端模式。很多的ARM,DSP都為小端模式。有些ARM處理器還可以由硬件來選擇是大端模式還是小端模式。
三、大小端字節序的判斷
判斷大小端其實很簡單,定義 int a = 1,16進制為 0x 00 00 00 01,我們只需要訪問其首地址對應的字節內容即可,小端會以 0x 01 00 00 00 從低地址往高地址排放,大端會以 0x 00 00 00 01 從低地址往高地址排放。
如下,一小段代碼即可:
#include <stdio.h>int check_sys()
{int a = 1;return *(char*)(&a);
}int main()
{int ret = check_sys();if (ret == 1){printf("小端\n");}else if (ret == 0){printf("大端\n");}return 0;
}
運行結果(VS):
四、字符型數據在內存中的存儲
字符型其實也屬于整形范疇,存儲的是其ASCII碼值。
我們可以先觀察以下代碼:
#include <stdio.h>int main()
{char a = -1;signed char b = -1;unsigned char c = -1;printf("a=%d, b=%d, c=%d\n", a, b, c);return 0;
}
運行結果:
解疑:
- 首先我們知道,字符型變量以%d打印時是要發生整形提升的。
- 然后a為char類型,vs中,char類型默認為有符號字符型,也就是等同于 signed char,所以a整形提升是要看符號位的,-1的補碼是11111111 11111111 11111111 11111111,a大小只有一個字節,存儲時會發生截斷取后8位,a其補碼為11111111,整形提升后 11111111 11111111 11111111 11111111,以%d打印的是原碼,再轉為原碼為10000000 00000000 00000000 00000001,因此打印的還是-1
- b與a一樣,然后就是c,c是無符號字符形,c的補碼還是1111111,整形提升,無符號整形提升高位補0,也就是 00000000 00000000 00000000 11111111,再轉為原碼,因為是無符號整形,原反補相同,所以原碼還是 00000000 00000000 00000000 11111111,打印出來就是255。
在看這段代碼:
#include <stdio.h>int main()
{char a = -128;printf("%u\n", a);printf("%d\n", a);return 0;
}
運行結果:
解疑:
- 我們發現以%u(無符號整形)方式打印時,打印出的是一個非常大的數字,以%d打印還是原數值,那么為什么會這樣呢
- 首先,a以%u打印時,還是會發生整形提升,-128有點大,我們先算出-128的原碼為? 10000000 00000000 00000000 10000000,取反加1算出-128的補碼為 11111111 11111111 11111111 10000000,截斷后 a 的補碼就是10000000,以%u打印,雖然是以無符號整形打印,但是整形提升時是根據原類型進行提升的,原類型為char,有符號字符型,所以高位補符號位1,即 11111111 11111111 11111111 10000000。然后%u就發揮作用了,要把整形提升后的這個補碼看成無符號整形,這時候原反補就相同,原碼就是 1111111 11111111 11111111 10000000,打印出來的結果就是上圖中很大的數字,我們可以借助計算器驗證:
- 以%d打印時接著2中整形提升后的補碼 11111111 11111111 11111111 10000000,這里是以%d打印,所以要看成有符號的整形,其原碼就要進行取反加1,即 10000000 00000000 00000000 10000000,打印出來就是-128
再看這段代碼:
#include <stdio.h>int main()
{char a = 128;printf("%u\n", a);printf("%d\n", a);return 0;
}
運行結果:
解疑:
- 我們發現128的結果與-128的結果相同,這又是為什么
- 其實我們自己再重新算一下就會發現,a存儲時補碼都是 10000000,因為128與-128的補碼后8位是完全相同的,截斷時值就相同。算一個特殊情況,因為a的值相同,所以后續以%u或者%d打印時效果也相同。
通過以上例題,我們可以再推導一下char型變量(有符號)的存儲范圍為啥是 -128~127了,還有 unsigned char 為啥是 0~255
我們畫圖分析:
如此,我們可以畫成一個圓:
這就是 char 類型為什么存儲范圍是-128~127,哪怕賦值的數字超過這個范圍,也會被截斷在這個范圍內。
這就是unsigned char 存儲范圍為什么是0~255。
其實,signed short 和 unsigned short 類型數據也可以畫圓圈表示,int也可以,這里如果感興趣可以自己畫著試試。同上即可
總結
? ? ? ? 以上就是本文的全部內容,希望對你有所幫助。