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

c語言整型和字符型的自動類型轉換

size \n 變量賦值 類型 結果 決定 特殊 code computer

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"。

c語言整型和字符型的自動類型轉換