這兩天和一個年前的同事一起配合完成一個項目,主要是他做個上位機,然後給我發報文,但是在發送報文的時候有點不清楚無符號和有符號之間的轉化,其實符號數和無符號數算是編程中較為基礎的部分,今天就來說說這裡面的門道道。
說到符號數,首先就得明確原碼,補碼,反碼這三個碼。計算機中的數不管是什麼數都是通過二進制補碼的形式來存儲的,無論是正數或者負數,整數還是小數都是通過二進制存儲。(小數一般稱作浮點數,目前很多CPU包括嵌入式系統都是支持浮點數運算的,今天主要講整數的情況) 而在內存中,數據都是以補碼形式存儲的,正數的補碼、反碼和原碼都是相同的,而負數的補碼則是其原碼取反加1,因此統一來說,其實數據都是以補碼形式存儲(雖然正數的補碼和原碼是一樣的)。
舉個例子,比如我定義一個char類型的變量,char a=12,它是正數,對應的二進制原碼是0000 1100,它的反碼也是0000 1100 ,它的補碼也是0000 1100,因此它存儲在內存中也就是0000 1100這個值。 如果我定義一個變量 char a =-12 ,它是負數,對應的二進制原碼是1000 1100(註意,一般對於有符號變量,第一位為符號位,0代表是正數而1代表是負數),它的反碼是1111 0011(符號位是不取反的),它的補碼則是1111 0100,因此這個數在內存中就是以1111 0100這個數來存儲的。
到目前為止還是比較清楚的,那接下來呢。如果我定義char a=241 ,char b=-15兩個變量,比較這兩個數的大小。乍一看,你可能想當然就認為a>b瞭,實則不然,這隻是人的慣性思維。然後細心的朋友一看,說這個定義有問題,問題在於a變量,因為定義瞭一個char類型變量,這個是有符號數變量,因為最高位是符號位,有效數值隻能從-127~127,因此定義瞭一個超過127或者小於-127的數都是錯的,能看出這點說明基礎還是很紮實的。然而在很多編譯器編譯時並不會報這個錯,最多報警告,有的編譯器甚至不報錯。這其實是一種數值的溢出,但是並不妨礙數據能夠被完整的保存下來,此時a被編譯器看作是個正數,其補碼等於原碼是1111 0001,b則被編譯器當作是負數,其原碼是1000 1111,補碼則是1111 0001,也就是說兩者在內存中的存儲的形式是一樣的,而對兩者進行比較大小時,此時編譯器對於變量a當作是有符號數並且是負數(因為內存中存儲的最高位是1),所以變量a被編譯器當作瞭數值是-15,因此結果竟是a=b。
那我現在換一個思路,如果是負數溢出呢,我定義一個char a=-200,char b=56兩個變量,同樣的,對於數值a,編譯器會當作是一個負數,其原碼是1 1100 1000,這裡為瞭體現其是負數,由於溢出特地在最高位補瞭數值1,其補碼則是1 0011 1000,此時在內存中就會把數據存儲為0011 1000,存儲的時候該數竟然成為瞭一個正數,而b變量是正數,存儲成0011 1000,因此此時比較時,編譯器又將這兩個值作為相等瞭,即a=b。(很多編譯器對於負數溢出時比較敏感的,一般這種情況,很多編譯器至少是要報警告的)
接下來我們再看一個例子:
得到的結果:
612ae73e0e258f65258beb768d25964f
首先看第一個判斷,根據上文我們的瞭解,a==b肯定是成立的。
再看第二個判斷,c=a+1,a在內存中存儲形式為1111 0001,因此用它進行+1操作時,編譯器其實是將數據a當作-15來進行運算的,因此c=-15+1,為-14,因此第二個判斷c==-14也是成立的。
再看第三個判斷,c作為一個變量,數值為-14,其在內存中的存放的是補碼形式,因此在內存中存放的格式為1111 0010,這麼一看好像比較的時候就是0xF2,實則不然,因為這裡的0xF2並不是一個變量,它本身並不存放在內存變量區域,因此0xF2實際就是242,-14顯然是不等於242這個數值的,因此c並不等於0xF2(記住這裡的重點是0xF2並不是一個變量,而是一個常量,並不是像之前存放在內存中的那些以補碼形式存放的變量)
第四個判斷就比較好理解瞭,d其實是溢出的一個變量,因此其存放以補碼形式存放,編譯器會將其以1111 0010存放,實際上編譯其就會將其作為-14來處理。因此c==d是成立的。
上面都是帶符號數的一些情況,接下來討論無符號數的情況。無符號數和有符號數表示的范圍不同,同樣定義一個unsigned char類型變量,這個變量能夠表示0~255之間的數,不在這個范圍內則同樣是屬於溢出的情況。定義一個unsigned char a=0xF1,unsigned char b=-15,對於變量a,編譯器會判定是正數,存放的時候就是1111 0001的形式,而對於變量b,顯示是屬於溢出的情況,但是編譯器會是將其先以負數的補碼形式存放,即1111 0001,而後將其拿出來和a比較時,由於定義的是unsigned char類型變量,因此最高位不在是符號位,所以b的值就等於0xF1,結論就是a=b。
再來看下面的例子:
fa449ebd91f5ab9777c57da774b4c289
得到的結果是:
第一個判斷上述已經討論過。
第二個判斷,c=b+1,b這個時候被編譯器編譯成瞭0xF1,因此肯定是不會等於-14的。
第三個判斷,c=b+1,0xF1+1,等於0xF2,因此成立。
第四個判斷同樣的情況。
這裡我們最後做個總結,對於定義的無符號數或者有符號數,無論是否溢出,編譯器都會首先將其以補碼的形式保存(正數等於其原碼,負數等於原碼取反+1),當變量在後面的程序操作時(比如運算、比較等),再會根據定義的變量的形式做操作,也就是其實這些操作都是編譯器來進行的,而對於CPU本身,其存放的都是數值的補碼,並不能判斷其數值的正負。所以當你定義一個unsigned char a=0xFE時,如果要將其轉化成char類型變量進行數據傳輸,可以進行強制轉換,因此char a=0xFE,在內存中存儲時也是以1111 1110的形式存儲的,並不妨礙。
本文謝絕轉載,雖然比較基礎,但是也是在編程的時候比較重要的,喜歡的話就點關註我吧,我會不定時更新一些文章,都是我在工程項目中一些遇到的。
《一》什么是DSD?这种格式有什么优缺点?DSD是Direct Stream Digital的缩写,是Sony与Philips推出SACD时所注册的商标。这种编 ...