摘要:數(shù)據(jù)的存儲(chǔ)前言數(shù)據(jù)類型匯總整型家族浮點(diǎn)型家族自定義類型指針類型。整型家族注在之后的標(biāo)準(zhǔn)規(guī)定,將類型數(shù)據(jù)劃分為整型家族,因?yàn)樽址趦?nèi)存中會(huì)將其轉(zhuǎn)化為碼值進(jìn)行存儲(chǔ)。
我們?cè)谇么a的時(shí)候總是會(huì)定義各種變量,對(duì)各種數(shù)據(jù)進(jìn)行存儲(chǔ),比如int a = 10;就是將10這個(gè)數(shù)據(jù)存放進(jìn)變量a中,而變量a,就是我們?cè)趦?nèi)存中申請(qǐng)開辟的一塊空間。
在內(nèi)存中如何開辟空間給變量的問題博主已經(jīng)在函數(shù)棧幀里用反匯編的方式將其原理剖析了,具體可看圖解函數(shù)棧幀 - 函數(shù)棧幀的創(chuàng)建及銷毀。
本文將進(jìn)一步剖析在已經(jīng)開辟好存儲(chǔ)單元的情況下,各種數(shù)據(jù)是如何存儲(chǔ)的。
在了解數(shù)據(jù)如何存儲(chǔ)之前,應(yīng)該先了解我們常見的數(shù)據(jù)類型。
在C99標(biāo)準(zhǔn)中,我們可將數(shù)據(jù)類型劃分為以下幾大類。
- 整型家族
- 浮點(diǎn)型家族(實(shí)型家族)
- 自定義類型(構(gòu)造類型)
- 指針類型
- 空類型
下面一一介紹這五種類型的基本情況。
char unsigned char signed charshort unsigned short [int] signed short [int]int unsigned int signed intlong unsigned long [int] signed long [int]
注:在C99之后的標(biāo)準(zhǔn)規(guī)定,將char類型數(shù)據(jù)劃分為整型家族,因?yàn)樽址趦?nèi)存中會(huì)將其轉(zhuǎn)化為ASCII碼值進(jìn)行存儲(chǔ)。
如上所示,所有的整型家族都被分為有符號(hào)整型和無符號(hào)整型,并且signed都是可以被省略的,換言之,signed int完全等價(jià)于int,其他以此類推,但其中有一個(gè)例外: char類型和signed char并不等價(jià),只寫一個(gè)char ch = 0;我們將無法分辨這個(gè)ch變量到底是有符號(hào)字符型還是無符號(hào)字符型,他完全取決于編譯器,但經(jīng)博主測試,大部分編譯器下char類型都被編譯器翻譯為有符號(hào)的char類型。
在C99中還引入了long long - 長長整型,用法和long類型一致,但C語言語法規(guī)定,sizeof(long)<= sizeof(long long),而long類型所占內(nèi)存大小為4/8字節(jié),所以long long類型所占內(nèi)存空間大小一定為8個(gè)字節(jié)。
floatdouble
浮點(diǎn)型家族只有float和double這兩種類型,float類型所占空間大小為4byte,double類型所占空間大小為8byte。
他們之間的區(qū)別除了所占空間大小不同之外還有精度的區(qū)別,float稱為單精度浮點(diǎn)型,有效精度為小數(shù)點(diǎn)后6位,而double類型稱為雙精度浮點(diǎn)型,精確到小數(shù)點(diǎn)后15位,但其有效數(shù)字只有11位左右。
> 數(shù)組類型> 結(jié)構(gòu)體類型 struct> 枚舉類型 enum> 聯(lián)合類型 union
這里可能會(huì)有很多人無法李姐為什么數(shù)組類型也被劃分為自定義類型,這里稍微做一些解釋。
我們知道數(shù)組類型的變量定義形式:數(shù)據(jù)類型+數(shù)組名+[數(shù)組大小];
如:
int arr[10] = { 0 };
這里可能會(huì)讓很多人產(chǎn)生誤區(qū),認(rèn)為arr數(shù)組的類型是int類型,也就把這條語句理解為是int類型的、數(shù)組名為arr的數(shù)組大小為10的數(shù)組,其實(shí)不然,這個(gè)數(shù)組的數(shù)組名確實(shí)是arr,但其數(shù)據(jù)類型是int [10],這里可能讓大部分人無法接受,
舉個(gè)簡單的例子即可解釋:
我們知道,sizeof操作符是用來計(jì)算所占內(nèi)存空間大小的,其操作數(shù)既可以是變量名,也可以是變量類型。
#define _CRT_SECURE_NO_WARNINGS 1#include int main(){ int a = 10; printf("%d/n", sizeof(a)); printf("%d/n", sizeof(int)); return 0;}
這兩種寫法都正確,打印結(jié)果為:
而對(duì)于數(shù)組,操作數(shù)也同樣可以是數(shù)組名或者數(shù)組類型:
#define _CRT_SECURE_NO_WARNINGS 1#include int main(){ /*int a = 10; printf("%d/n", sizeof(a)); printf("%d/n", sizeof(int));*/ int arr[10] = { 0 }; printf("%d/n", sizeof(arr)); printf("%d/n", sizeof(int[10])); return 0;}
其打印結(jié)果為:
這么一來,就驗(yàn)證了int [10]是數(shù)組類型。
知道了這點(diǎn),解釋為什么數(shù)組類型是自定義類型就更清晰了,用上面解釋的結(jié)論就可以知道,int arr[10]和int arr[9]的數(shù)組類型不同,并不都是int類型的,數(shù)組大小是我們程序員人為規(guī)定的,所以可以把他劃分為自定義類型。
其他的自定義類型比較明顯,這里就不一一解釋。
指針類型很特殊。
我們常說的指針有兩個(gè)含義:
指針類型的定義方式為:
數(shù)據(jù)類型+*(用于標(biāo)識(shí)指針類型)+指針變量名
常見的指針類型有:
int* pi;char* pc;float* pf;void* pv;
這里著重介紹一點(diǎn),指針變量賦值大部分都是取出某變量地址存放進(jìn)指針變量,如int pc = &c;
但有一個(gè)例外:
int main(){ char* pc = "hello world"; printf("%c/n", *pc); return 0;}
這里之間將一個(gè)字符串常量賦值給指針變量pc,我們知道,字符串常量時(shí)放在常量區(qū)的,他的值不可修改,并且這里的字符串加上隱藏的’/0’總共是12個(gè)字節(jié),而我們的指針變量根據(jù)平臺(tái)的不同只能是4/8個(gè)字節(jié),怎么都不可能放的下這個(gè)字符串常量,所以這么理解是錯(cuò)誤的。
我們將其打印看看結(jié)果:
打印結(jié)果為單字母h,這么一來其實(shí)就解釋的通了,將整個(gè)常量字符串賦值給指針變量,其實(shí)并不會(huì)把整個(gè)字符串放進(jìn)去,而是把整個(gè)字符串的首地址賦給指針變量,比較指針存放的就是地址,這和將字符數(shù)組名賦值給指針變量類似,存放的都是首元素地址。
void 用于表示空類型(無類型)
通常應(yīng)用于函數(shù)的返回類型、函數(shù)的參數(shù)、指針類型。
下面舉幾空類型的例子幫助理解:
void test(int x){ printf("%d/n", x);}int main(){ int a = 10; test(a); return 0;}
這里test函數(shù)的返回類型就是void。
int test(void){ return 1;}int main(){ int ret = test(); printf("%d/n", ret); return 0;}
這個(gè)代碼就是將函數(shù)的參數(shù)置為空,表示不允許主調(diào)函數(shù)傳參,如果非要傳參,編譯器將給出警告。
int test(void){ return 1;}int main(){ int a = 10; int ret = test(a); printf("%d/n", ret); return 0;}
void* pc;
表示定義一個(gè)指針pc,但他什么都不指向,作為一個(gè)空指針存在。
我們知道不管是什么樣的數(shù)據(jù),最終都會(huì)被編譯器編譯為二進(jìn)制機(jī)器碼進(jìn)行存儲(chǔ),并且我們的內(nèi)存是以字節(jié)為最小存儲(chǔ)單元?jiǎng)澐侄M(jìn)行存儲(chǔ)的,那么就存在了一個(gè)問題,數(shù)據(jù)以字節(jié)為單位進(jìn)行存儲(chǔ)的時(shí)候,是以怎樣的順序進(jìn)行存儲(chǔ)的呢?這就引出了大小端字節(jié)序的概念。
為什么會(huì)有大小端字節(jié)序模式之分呢?這是因?yàn)樵谟?jì)算機(jī)系統(tǒng)中,我們是以字節(jié)為單位的,每個(gè)地址單元都對(duì)應(yīng)著一個(gè)字節(jié),一個(gè)字節(jié)為8bit位。但是在C語言中除了8bit的char類型之外,還有16bit的short類型,32bit的long類型(要看具體的編譯器,64位平臺(tái)long類型為64位),另外,對(duì)于位數(shù)大于8位的處理器,例如16位或者32位的處理器,由于寄存器的寬度大于一個(gè)字節(jié),那么必然存在著一個(gè)如何將多個(gè)字節(jié)安排的問題。因此就導(dǎo)致了大端存儲(chǔ)模式和小端存儲(chǔ)模式。
例如:一個(gè)16bit位的short類型變量x ,在內(nèi)存中的地址為0x0010,變量x 的值為0x1122 ,那么0x11為高字節(jié),0x22為低字節(jié)。對(duì)于大端模式,就將 0x11放在低地址中,即0x0010中,0x22 放在高地址中,即0x0011中。小端模式,剛好相反。我們常用的X86(32位平臺(tái))結(jié)構(gòu)是小端模式,而KEILC51則為大端模式。很多的ARM,DSP都為小端模式。有些ARM處理器還可以由硬件來選擇是大端模式還是小端模式。
字節(jié)序,即字節(jié)順序,又稱端序或尾序,在計(jì)算機(jī)科學(xué)領(lǐng)域中,指「存儲(chǔ)器」中或者「數(shù)字通信鏈路」中,組成多字節(jié)的字節(jié)排列順序 。在幾乎所有的機(jī)器上,多字節(jié)對(duì)象都被存儲(chǔ)為連續(xù)的字節(jié)序列 。例如在C語言中,一個(gè) int類型的變量x地址為0x100,那么其對(duì)應(yīng)的地址表達(dá)式&x的值為0x100 且 x 的4個(gè)字節(jié)將被存儲(chǔ)在存儲(chǔ)器的0x100, 0x101, 0x102, 0x103位置。字節(jié)的排列方式有2個(gè)通用規(guī)則。
- 順序排列 - 大端字節(jié)序
- 逆序排列 - 小端字節(jié)序
上面的文字描述也許過于抽象,接下來用較為容易理解的方式分別簡單的介紹大端字節(jié)序和小端字節(jié)序的概念。
所謂大小端字節(jié)序,就是將多字節(jié)數(shù)據(jù)中的高低字節(jié)位按不同順序存放在內(nèi)存中的高低地址處,相當(dāng)于順(逆)序存放。接下來博主將把上述抽象概念劃分逐一介紹:
我們知道一個(gè)數(shù)據(jù)根據(jù)大小不同被劃分為不同的數(shù)據(jù)類型,各數(shù)據(jù)類型所占字節(jié)數(shù)不同,我們也就據(jù)此根據(jù)數(shù)據(jù)字節(jié)大小來將其存放于不同的數(shù)據(jù)類型中。
比如字符類型 - 其擴(kuò)展之后的ASCII碼值為0~255,我們知道一個(gè)字節(jié)是8位,按照無符號(hào)字符型的理解也就是從00000000 ~ 11111111,剛好是0 ~ 255,所以字符類型被稱為單字符類型數(shù)據(jù)。
而十六進(jìn)制數(shù),如:0x11223344則為多字節(jié)數(shù)據(jù),其中有4個(gè)字節(jié),分別是0x11、0x22、0x33、0x44,像這樣的數(shù)據(jù)則被稱為多字節(jié)數(shù)據(jù)。
在一個(gè)二進(jìn)制序列中,
如:01010110101001011010100101101001
我們把前方高亮部分的0101稱為高字節(jié)位,把后端加刪除線的1001 部分稱為低字節(jié)位,以此區(qū)分。
其實(shí)很好理解,因?yàn)樽詈笠粋€(gè)1的的權(quán)重為20,也就是2的0次方,而第一個(gè)0的權(quán)重為231,也就是2的31次方,以此來區(qū)分高低字節(jié)位也是很不錯(cuò)的選擇。
接下來介紹大小端字節(jié)序的存儲(chǔ)方式:
大端字節(jié)序
所謂大端字節(jié)序,就是將處于高字節(jié)位的數(shù)據(jù)存放在內(nèi)存的低地址處,將處于低字節(jié)位的數(shù)據(jù)存放在內(nèi)存的高地址處
如今給一數(shù)據(jù):0x11223344
在內(nèi)存中的存放形式為:
以這樣的形式存放的模式,就稱為大端存儲(chǔ)模式,這樣的存放順序,也就被稱為大端字節(jié)序。
小端字節(jié)序
所謂小端字節(jié)序,就是將處于高字節(jié)位的數(shù)據(jù)存放在內(nèi)存的高地址處,將處于低字節(jié)位的數(shù)據(jù)存放在內(nèi)存的低地址處
今給一數(shù)據(jù):0x11223344
在內(nèi)存中的存放形式為:
以這樣的形式存放的模式,就稱為小端存儲(chǔ)模式,這樣的存放順序,也就被稱為小端字節(jié)序。
在博主使用的VS2019編譯器上,采用的就是小端字節(jié)序:
例:
int main(){ int a = 0x0000ff40; return 0;}
調(diào)試 - 內(nèi)存窗口(&a):
0x001DFEFC就是該代碼中a變量的地址,存放情況為40 ff 00 00。
也就是小端存儲(chǔ)模式。
百度2015年系統(tǒng)工程師筆試題:
請(qǐng)簡述大端字節(jié)序和小端字節(jié)序的概念,設(shè)計(jì)一個(gè)小程序來判斷當(dāng)前機(jī)器的字節(jié)序。(10分)
該題前半部分在上文其實(shí)已經(jīng)解決了,這里博主將分析問題,并實(shí)現(xiàn)代碼。
要判斷編譯系統(tǒng)到底是大端存儲(chǔ)還是小端存儲(chǔ),其實(shí)并不復(fù)雜。
如0x11223344
如果是在大端存儲(chǔ)模式下:
存儲(chǔ)方式為:11 22 33 44
如果是在小端存儲(chǔ)模式下:
存儲(chǔ)方式為:44 33 22 11
所以其實(shí)只需要知道第一個(gè)字節(jié)的內(nèi)容到底是11還是44就可以判斷了。
但這樣的數(shù)據(jù)太過于復(fù)雜,不如換簡單一點(diǎn)的數(shù)字,比如1。
1的高字節(jié)位就是00,低字節(jié)位就是01,比較好判斷。
int check_sys(int x){ return *(char*)&x;}int main(){ int a = 1; //約定: //如果是大端,返回0 //如果是小端,返回1 int ret = check_sys(a); if (ret) { printf("是小端存儲(chǔ)模式/n"); } else { printf("是大端存儲(chǔ)模式/n"); } return 0;}
運(yùn)行結(jié)果:
之前也分析了,我的編譯器VS2019是小端存儲(chǔ)模式,所以代碼的結(jié)果正確,下面分析代碼。
想要在4個(gè)字節(jié)中拿到第一個(gè)字節(jié),只需要在取地址時(shí)將整型強(qiáng)制類型轉(zhuǎn)換為字符型即可,拿到存放第一個(gè)字節(jié)的地址后對(duì)其解引用便可拿到第一個(gè)字節(jié)數(shù)據(jù)。
如果拿到的是01,說明存儲(chǔ)方式是01 00 00 00,也就是小端存儲(chǔ)模式,反之則為大端存儲(chǔ)模式。
這里如果有沒有講清楚的地方,歡迎評(píng)論區(qū)留言或者私信博主解決嗷。
數(shù)據(jù)在內(nèi)存中的存儲(chǔ)遵循一定的法則,而整型數(shù)據(jù)和浮點(diǎn)型數(shù)據(jù)在內(nèi)存中所遵循的法則是不同的,這里我們先介紹整型數(shù)據(jù)在內(nèi)存中是如何存儲(chǔ)的。
介紹整型數(shù)據(jù)的存儲(chǔ)需要先引進(jìn)一個(gè)概念:原反補(bǔ)碼。
計(jì)算機(jī)中的有符號(hào)數(shù)有三種表示方法,即原碼、反碼和補(bǔ)碼。三種表示方法均有符號(hào)位和數(shù)值位(或稱有效位)兩部分,符號(hào)位都是用0表示“正”,用1表示“負(fù)”,而數(shù)值位,三種表示方法各不相同。在計(jì)算機(jī)系統(tǒng)中,數(shù)值一律用補(bǔ)碼來表示和存儲(chǔ)。原因在于:使用補(bǔ)碼,可以將符號(hào)位和數(shù)值域統(tǒng)一處理;同時(shí),加法和減法也可以統(tǒng)一處理。
而補(bǔ)碼其實(shí)是針對(duì)負(fù)數(shù)存儲(chǔ)設(shè)定的,對(duì)于無符號(hào)數(shù)來說,其反碼和補(bǔ)碼都和原碼相等。
原碼:
所謂原碼,就是將數(shù)據(jù)直接翻譯為二進(jìn)制序列。
拿32位平臺(tái)舉例,最高位作為符號(hào)位,正數(shù)的符號(hào)位為0,負(fù)數(shù)的符號(hào)位為1,后面的31位稱為有效位,以不同的權(quán)重計(jì)算出不同的數(shù)字,最低位的權(quán)重為20,其次為21,以此類推。
如:
13的原碼為:00000000000000000000000000001101-3的原碼為:10000000000000000000000000000011
反碼:
反碼,顧名思義,就是將原碼的二進(jìn)制序列按位取反,但這里需要注意,并不是將所有的二進(jìn)制位都按位取反,符號(hào)位是特殊獨(dú)立出來的,他表示一個(gè)數(shù)的正負(fù),隨意取反可能會(huì)遭遇意想不到的結(jié)果。
所以反碼應(yīng)該通過原碼除符號(hào)位,其他位按位取反獲得。
(注:正數(shù)的反碼和原碼相等。)
如:
13的反碼為:00000000000000000000000000001101-3的反碼為:11111111111111111111111111111100
補(bǔ)碼:
整數(shù)在內(nèi)存中的存儲(chǔ)存的都是補(bǔ)碼,所以要通過上面的反碼求出補(bǔ)碼,補(bǔ)碼的獲取規(guī)則是原碼按位取反(除符號(hào)位)再加一。
(注:正數(shù)的補(bǔ)碼和原碼相等。)
如:
13的補(bǔ)碼為:00000000000000000000000000001101-3的補(bǔ)碼為:11111111111111111111111111111101
因?yàn)檎麛?shù)在內(nèi)存中的存儲(chǔ)形式是補(bǔ)碼,所以引出原反補(bǔ)的意義就是求出補(bǔ)碼,而補(bǔ)碼的計(jì)算公式為:補(bǔ)碼 = 原碼按位取反(除符號(hào)位)再加一
這里我們通過VS2019編譯器進(jìn)行驗(yàn)證內(nèi)存中存儲(chǔ)的是數(shù)據(jù)的補(bǔ)碼:
int main(){ int a = 13; //原碼:00000000 00000000 00000000 00001101 //反碼:01111111 11111111 11111111 11110010 //補(bǔ)碼:01111111 11111111 11111111 11110011 int b = -3; //原碼:10000000 00000000 00000000 00000011 //反碼:11111111 11111111 11111111 11111100 //補(bǔ)碼:11111111 11111111 11111111 11111101 return 0;}
編譯器下調(diào)試 - 內(nèi)存 - &a:
內(nèi)存中存儲(chǔ)的是:0d 00 00 00
為小端存儲(chǔ)模式,00001101轉(zhuǎn)換為十六進(jìn)制就是0d。
編譯器下調(diào)試 - 內(nèi)存 - &b:
內(nèi)存中存儲(chǔ)的是:fd ff ff ff
為小端存儲(chǔ)模式,1111 1111轉(zhuǎn)換為十六進(jìn)制就是ff,1111 1101轉(zhuǎn)換為十六進(jìn)制就是fd。
如此說來,在內(nèi)存中真的存放的就是補(bǔ)碼,所以為了弄清楚整型數(shù)據(jù)在內(nèi)存中的存儲(chǔ),必須牢牢掌握原反補(bǔ)的概念。
我們知道int類型的變量所占空間大小是4個(gè)字節(jié)32個(gè)bit位(32位平臺(tái)下),而char類型的變量所占空間大小是1個(gè)字節(jié)8個(gè)bit位,那我要怎么將一個(gè)整型的數(shù)據(jù)存放在一個(gè)char類型的變量里呢?這里教大家一個(gè)很有用的辦法,那就是沒辦法,32個(gè)比特位是不可能放進(jìn)8個(gè)小格子里的,所以就會(huì)發(fā)生所謂的截?cái)?/strong>。
我們知道,一個(gè)char類型只能存放8個(gè)比特位,那如果我要將char類型的數(shù)據(jù)以%d的形式打印,也就是看做32位數(shù)據(jù)將其打印,那有要怎么做呢?再教大家一個(gè)辦法,那依然是沒辦法,所以編譯器只能對(duì)char類型的數(shù)據(jù)進(jìn)行整型提升。
接下來簡單講解截?cái)嗪驼吞嵘脑怼?/p>
截?cái)?/mark>
假設(shè)我有一個(gè)32位二進(jìn)制序列:
01010011001000110001000100100011
這是一個(gè)非常大的數(shù)字:
有一個(gè)char類型的空間:
在把32位數(shù)字往里放的時(shí)候會(huì)發(fā)現(xiàn)放不下,便會(huì)發(fā)生截?cái)啵槐A舻桶宋坏臄?shù)字,其他24位數(shù)字直接舍棄,
最終存放的結(jié)果為:
這就是截?cái)嗟倪^程。
整型提升
當(dāng)我要將char類型的數(shù)據(jù)以%d的形式打印時(shí),我們知道,%d是打印有符號(hào)整型,打印的是32位0/1序列的最終結(jié)果,但我們的char類型里只存放了8位,這個(gè)時(shí)候就會(huì)發(fā)生整型提升。
整型提升規(guī)則:
如:
今有一8位無符號(hào)數(shù)。
unsigned char a = 148;
首先我們寫出該數(shù)的二進(jìn)制序列。
10010100 - 148
由于變量a是無符號(hào)類型的,所以不管該二進(jìn)制序列首元素是0還是1,都將全部補(bǔ)0
獲得:
00000000000000000000000010010100
最終打印的結(jié)果就是148
對(duì)以下代碼分析輸出結(jié)果:
1.//輸出什么?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;}
首先VS2019編譯器對(duì)char類型的處理為默認(rèn)認(rèn)為是有符號(hào)的char,所以變量a和變量b屬于同一類型。
先計(jì)算出-1的補(bǔ)碼。
int main(){ //-1 //原碼:10000000000000000000000000000001 //反碼:11111111111111111111111111111110 //補(bǔ)碼:11111111111111111111111111111111 char a = -1; signed char b = -1; unsigned char c = -1; printf("a=%d b=%d c=%d/n", a, b, c); return 0;}
三個(gè)變量都是char類型,所以存儲(chǔ)時(shí)都將發(fā)生截?cái)?/mark>。
int main(){ //-1 //原碼:10000000000000000000000000000001 //反碼:11111111111111111111111111111110 //補(bǔ)碼:11111111111111111111111111111111 char a = -1; //存儲(chǔ)的補(bǔ)碼:11111111 signed char b = -1; //存儲(chǔ)的補(bǔ)碼:11111111 unsigned char c = -1; //存儲(chǔ)的補(bǔ)碼:11111111 printf("a=%d b=%d c=%d/n", a, b, c); return 0;}
現(xiàn)在要將三個(gè)變量以%d形式打印,則會(huì)發(fā)生整型提升。
變量a和變量b整型提升后的結(jié)果為:
11111111111111111111111111111111
變量c整型提升后的結(jié)果為:
00000000000000000000000011111111
因?yàn)樘嵘蟮腸符號(hào)位是0,所以原反補(bǔ)碼均相等。
而按%d形式打印需要將補(bǔ)碼轉(zhuǎn)化為原碼后轉(zhuǎn)化為十進(jìn)制進(jìn)行打印,
所以:
int main(){ //-1 //原碼:10000000000000000000000000000001 //反碼:11111111111111111111111111111110 //補(bǔ)碼:11111111111111111111111111111111 char a = -1; //存儲(chǔ)的補(bǔ)碼:11111111 //提升后的補(bǔ)碼:11111111111111111111111111111111 //提升后的反碼:10000000000000000000000000000000 //提升后的原碼:10000000000000000000000000000001 signed char b = -1; //存儲(chǔ)的補(bǔ)碼:11111111 //提升后的補(bǔ)碼:11111111111111111111111111111111 //提升后的反碼:10000000000000000000000000000000 //提升后的原碼:10000000000000000000000000000001 unsigned char c = -1; //存儲(chǔ)的補(bǔ)碼:11111111 //提升后的補(bǔ)碼:00000000000000000000000011111111 //提升后的反碼:00000000000000000000000011111111 //提升后的原碼:00000000000000000000000011111111 printf("a=%d b=%d c=%d/n", a, b, c); return 0;}
這么一來,打印的結(jié)果就應(yīng)該是-1 -1 255
打印結(jié)果:
2.int main(){ char a = -128; printf("%u/n", a); return 0;}
這道題的變量a是有符號(hào)的char類型的。
首先計(jì)算出-128的原反補(bǔ)碼。
int main(){ char a = -128; //-128 //原碼:10000000000000000000000010000000 //反碼:11111111111111111111111101111111 //補(bǔ)碼:11111111111111111111111110000000 printf("%u/n", a); return 0;}
將01111111111111111111111110000000這樣一個(gè)二進(jìn)制序列存放進(jìn)a中將會(huì)發(fā)生截?cái)?/mark>。
截?cái)嘀骯中存放的結(jié)果為:10000000
這時(shí)以%u的形式打印,也就是以無符號(hào)整型的形式打印,要進(jìn)行整型提升,而變量a是一個(gè)有符號(hào)的char類型,第一個(gè)元素是1,所以整型提升24個(gè)1。
int main(){ char a = -128; //-128 //原碼:10000000000000000000000010000000 //反碼:11111111111111111111111101111111 //補(bǔ)碼:11111111111111111111111110000000 //截?cái)嗟慕Y(jié)果:10000000 //整型提升后的結(jié)果:11111111111111111111111110000000 printf("%u/n", a); return 0;}
這時(shí)要將提升之后的補(bǔ)碼轉(zhuǎn)換為原碼后以十進(jìn)制的形式進(jìn)行打印。
而%u的形式將把補(bǔ)碼中的符號(hào)位看做是有效位,所以其原反補(bǔ)都是一樣的。
int main(){ char a = -128; //-128 //原碼:10000000000000000000000010000000 //反碼:11111111111111111111111101111111 //補(bǔ)碼:11111111111111111111111110000000 //截?cái)嗟慕Y(jié)果:10000000 //整型提升后的結(jié)果:11111111111111111111111110000000 //補(bǔ)碼:11111111111111111111111110000000 //反碼:11111111111111111111111110000000 //原碼:11111111111111111111111110000000 printf("%u/n", a); return 0;}
而11111111111111111111111110000000的值應(yīng)該是4,294,967,168
所以輸出結(jié)果:
3.int main(){ char a = 128; printf("%u/n", a); return 0;}
還是一樣,先求出128的補(bǔ)碼,由于128是正數(shù),所以其原反補(bǔ)都是相同的為:
00000000000000000000000010000000
存放進(jìn)變量a中將發(fā)生整型截?cái)啵?/p>
10000000
而變量a為有符號(hào)的char類型,所以整型提升為
11111111111111111111111110000000
變量a以%u形式打印,則把符號(hào)位看成有效位,則此時(shí)原碼反碼補(bǔ)碼相同,直接進(jìn)行計(jì)算,11111111111111111111111110000000的十進(jìn)制形式為4,294,967,168
所以打印結(jié)果為:
4.int mian(){ int i = -20; unsigned int j = 10; //按照補(bǔ)碼的形式進(jìn)行運(yùn)算,最后格式化成為有符號(hào)整數(shù) printf("%d/n", i + j); return 0;}
還是先把-20和10的補(bǔ)碼計(jì)算出來,但是這里的i和j都是整型變量,所以不會(huì)發(fā)生截?cái)嗪驼吞嵘?/p>
int mian(){ int i = -20; //-20 //原碼:10000000000000000000000000010100 //反碼:11111111111111111111111111101011 //補(bǔ)碼:11111111111111111111111111101100 unsigned int j = 10; //10 //補(bǔ)碼:00000000000000000000000000001010 //按照補(bǔ)碼的形式進(jìn)行運(yùn)算,最后格式化成為有符號(hào)整數(shù) printf("%d/n", i + j); return 0;}
數(shù)據(jù)的計(jì)算是按照二進(jìn)制補(bǔ)碼的形式進(jìn)行計(jì)算的,最后的結(jié)果再根據(jù)打印要求或者存儲(chǔ)要求進(jìn)行調(diào)整更改。
計(jì)算的結(jié)果:
int mian(){ int i = -20; //-20 //原碼:10000000000000000000000000010100 //反碼:11111111111111111111111111101011 //補(bǔ)碼:11111111111111111111111111101100 unsigned int j = 10; //10 //補(bǔ)碼:00000000000000000000000000001010 //計(jì)算: //11111111111111111111111111101100 //00000000000000000000000000001010 //11111111111111111111111111110110 - 補(bǔ)碼相加的結(jié)果 //按照補(bǔ)碼的形式進(jìn)行運(yùn)算,最后格式化成為有符號(hào)整數(shù) printf("%d/n", i + j); return 0;}
要求按%d的形式打印,則將計(jì)算的結(jié)果轉(zhuǎn)化為原碼以有符號(hào)十進(jìn)制數(shù)打印。
補(bǔ)碼:11111111111111111111111111110110反碼:10000000000000000000000000001001原碼:10000000000000000000000000001010
計(jì)算結(jié)果為-10
int main(){ unsigned int i; for (i = 9; i >= 0; i--) { printf("%u/n", i); } return 0;}
程序分析:
變量i從9開始自減到0時(shí),都可以正常進(jìn)入程序打印的值就是
9 8 7 6 5 4 3 2 1 0
在打印完0之后,變量i再自減1,變成-1,按道理來說應(yīng)該跳出循環(huán),但我們注意,這里的變量i為無符號(hào)整型,而-1的補(bǔ)碼為11111111111111111111111111111111,所以會(huì)被解析為一個(gè)特別大的正整數(shù):4294967295。
那么他也符合循環(huán)控制條件(i >= 0),所以循環(huán)會(huì)繼續(xù)4294967295次,而一直自減到0的時(shí)候,再次自減又變成-1,有被解析為4294967295,所以該程序?qū)o限循環(huán)下去。
這里博主隨便截兩張打印結(jié)果的圖供大家參考。
6.#include <
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/122193.html
摘要:在符號(hào)位中,表示正,表示負(fù)。我們知道對(duì)于整型來說,內(nèi)存中存放的是該數(shù)的補(bǔ)碼。在計(jì)算機(jī)系統(tǒng)中,數(shù)值一律用補(bǔ)碼來表示和存儲(chǔ)。表示有效數(shù)字,。規(guī)定對(duì)于位的浮點(diǎn)數(shù),最高的位是 ...
摘要:的理解和區(qū)別代表有符號(hào),整數(shù)在內(nèi)存中存儲(chǔ)的二進(jìn)制位的最高位為符號(hào)位,表示負(fù)數(shù),表示正數(shù)。那接下來我們來學(xué)習(xí)數(shù)據(jù)在所開辟的內(nèi)存空間時(shí)如何存儲(chǔ)的。請(qǐng)看下面例子為什么內(nèi)存中存儲(chǔ)的是補(bǔ)碼對(duì)于整數(shù)來說數(shù)據(jù)存放內(nèi)存中其實(shí)存放的是補(bǔ)碼。 ...
摘要:講解從三個(gè)部分展開短視頻應(yīng)用場景阿里云短視頻解決方案阿里云對(duì)短視頻用戶體驗(yàn)的相關(guān)優(yōu)化。同時(shí),為了面對(duì)業(yè)務(wù)的突發(fā)流量,阿里云提供了超過的帶寬儲(chǔ)備,為持續(xù)增長的業(yè)務(wù)保駕護(hù)航。二播放卡頓是指在播放過程中的不流暢情況,會(huì)嚴(yán)重影響用戶體驗(yàn)。 深圳云棲大會(huì)已經(jīng)圓滿落幕,在3月29日飛天技術(shù)匯-彈性計(jì)算、網(wǎng)絡(luò)和CDN專場中,阿里云CDN高級(jí)技術(shù)專家周哲為我們帶來了《海量短視頻極速分發(fā)》的主題分享,帶...
摘要:接上篇合約升級(jí)模式介紹筆者改寫了一個(gè)可用于實(shí)踐生產(chǎn)的升級(jí)框架,需要自取。在介紹合約升級(jí)模式中提到了一個(gè)可以解決這個(gè)問題的方法。深度理解注意為中的低階方法下文中出現(xiàn)的方法,是我在智能合約中寫的一個(gè)方法名稱,不要混淆。 接上篇:合約升級(jí)模式介紹筆者改寫了一個(gè)可用于實(shí)踐生產(chǎn)的升級(jí)框架,需要自取。https://github.com/hammewang/... 同時(shí)歡迎討論,微信xiuxiu1...
閱讀 781·2023-04-25 16:55
閱讀 2817·2021-10-11 10:59
閱讀 2081·2021-09-09 11:38
閱讀 1795·2021-09-03 10:40
閱讀 1493·2019-08-30 15:52
閱讀 1133·2019-08-30 15:52
閱讀 963·2019-08-29 15:33
閱讀 3505·2019-08-29 11:26