1. 程式人生 > >c語言整型和字元型的自動型別轉換

c語言整型和字元型的自動型別轉換

char a = -1; //機器碼為0xff
unsigned char b = 254; //機器碼0xfe
if (a <= b){
  printf("a <= b\n");
}
else{
  printf("a > b\n");
}

  上述程式碼輸出結果: a > b

賦值用機器碼寫入記憶體

   雖然我們以十進位制為兩個變數賦值,但是變數值在記憶體中是以二進位制機器碼的形式存在。如果十進位制數是負數,它就以補碼的形式存放在記憶體中。比如"a = -1",a的真值以二進位制表示為"1000 0001",高位是符號位,其餘位表示絕對值;它的反碼是"1111 1110",補碼是"1111 1111",所以記憶體中某個存放變數a的位元組的數是0xff。而正數的補碼就是原碼,不需要轉換,所以記憶體中某個存放變數b的位元組的數是0xfe。(有關機器碼和補碼知識請戳

https://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html

執行時不同型別變數的比較存在型別轉換

  當正在比較的兩個變數型別不同時,會發生型別轉換。有符號char型和無符號char型比較時,有符號臨時轉換成無符號(機器碼不變,只是編譯器處理這個變數的方法改變)。a臨時轉成無符號後機器碼仍然時0xff,但是編譯器把它作為無符號處理——即沒有符號位,取值範圍時[0, 255],所以臨時變數值是255,自然比b大。

  

  那麼字元型和整型變數發生型別轉換時需要注意哪些呢?

  一位元組“字元型” -> (轉換為)四位元組“整型”,位元組數較少的字元型變數會向高位擴充套件,具體補‘0‘還是補’1‘,根據字元型變數自身型別和高位符號兩者決定。下面看四個例子。

例一:

char a = 0xff;
unsigned b = 0xffffffff;
if (a == b){
  printf("equal.\n");  
}
else{
  printf("not equal\n");  
}

上述程式碼輸出結果:equal.即補‘1’.

例二:

char a = 0xff;
int b = 0xffffffff;
if (a == b){
  printf("equal.\n");  
}
else{
  printf("not equal\n");  
}

上述程式碼輸出結果:equal.即補‘1’.

例二和例一隻有變數b的型別不同,由此看出向高地址補位的動作不受要轉向的那個型別所影響。

例三:

unsigned char a = 0xff;
unsigned b = 0xffffffff;
if (a == b){
  printf("equal.\n");  
}
else{
  printf("not equal.\n");  
}

上述程式碼輸出結果:not equal.。即補‘0’。

例三和例一隻有變數a的型別不同,由此看出向高地址補位的動作受變數本身型別所影響。

例四:

char a = 0x7f;
unsigned b = 0xffffffff;
if (a == b){
  printf("equal.\n");  
}
else{
  printf("not equal.\n");  
}

上述程式碼輸出結果:not equal.。即補‘0’。

例四和例一隻有變數a的值不一樣,例四中變數a的高位是0,因此向高位補‘0’,由此有符號型向高地址補位的動作受變數符號位的值所影響。

 

  而四位元組“整型”  -> (轉換為) 一位元組“字元型” ,就是單純地把低位一位元組的內容賦值給字元型變數。

char型資料溢位情況

char a = 64;
a *= 2;
if (a >= 0){
  printf("a >= 0");  
}
else{
  prinf("a < 0");  
}

上述程式碼輸出結果:a < 0。

  雖然以十進位制數‘128’賦值給變數,但實際存入記憶體中的機器碼是0x80,編譯時以有符號字元型處理這個位元組。這個值符號位是‘1’,表示負數,對其餘位求補碼——結果換算成十進位制,並加負號,就是這個機器碼的真值,即‘-128’。所以小於‘0’。例子中雖然0x80在一個位元組所能表示的數值範圍內,但是超過char型所能表示的正數範圍,這是char型資料溢位的一個例子。

unsigned char型資料溢位情況

unsigned char a = 128;
do {
    a *= 2;
    printf("%x", a); 
} while (a <= 256)

上述程式碼會不停迴圈。

  當變數a從0x80乘2後,機器碼是0x100。由於‘a’只能儲存一個位元組的資料,所以取結果的低位一位元組,即0x00,這樣從0 -> 255 -> 0迴圈下去。這是unsigned char型資料溢位的一個例子。

另外舉一個誤把unsigned char型當作負數處理地例子,雖然不可能發生,但有必要了解一下其中原因:

unsigned char a = 0x0a;
do {
    --a;
    printf("%x", a); 
} while (a >= 0)

上述程式碼會不停迴圈。

  當變數a從0x0自減後,機器碼是0xff。因為計算機運算中把減法當作兩數的補碼相加來做,(0 - 1)表示式在計算機運算中解釋為(0x0 + 0xff),所以結果是0xff。

最後舉一個char型最小負數取相反數溢位的例子:

 

char a = -128;
char b = -a;
if (b > 0){
  printf("b > 0\n");  
}
else{
  printf("b <= 0\n");  
}

 

上述程式碼輸出結果:b <= 0。

  |a|的真值用二進位制表示"1000 0000",用補碼錶示同樣是"1000 0000",最後由於是負數,高位置為‘1’,結果是"1000 0000",這個0x80的char型機器碼的特殊之處在於符號位同時表示數值。‘b’被編譯器處理為-128,所以輸出"b <= 0"。