第七章
有符號整數溢出,程序行為是未定義的
無符號整數運算過程中發生溢出,結果是有定義的:對2^n取模,n適用于存儲結果的位數
C標準未說明
char
類型是有符號還是無符號型-
「慣用法」
while(getchar()!='\n');//skip rest of line
while((c=getchar())==' ');//skip blanks
-
C89算術類型
- 整值類型
- 字符類型
char
- 有符號整型
signed char
、short int
、int
、long int
- 無符號整型
unsigned char
、unsigned short
、unsigned int
、unsigned long
- 枚舉類型
- 字符類型
- 浮點類型
float
、double
、long double
- 整值類型
-
C99算術類型
- 整數類型
- 字符類型
char
- 有符號整型 包括標準的(
signed char
、short int
、int
、long int
、long long int
)和擴展的 - 無符號整型 包括標準的(
unsigned char
、unsigned short int
、unsigned int
、unsigned long int
、unsigned long long int
、_Bool
)和擴展的 - 枚舉類型
- 字符類型
- 浮點類型
- 實數浮點類型
float
、double
、long double
- 復數類型
float_Complex
、double _Complex
、long double _Complex
- 實數浮點類型
- 整數類型
-
以下情況進行隱式轉換
- 當算術表達式或邏輯表達式中操作數的類型不相同時(C語言執行所謂的常用算術轉換)
- 當賦值運算符的右側表達式類型與左側變量的類型不匹配時
- 當函數調用的實參類型與形參類型不匹配時
- 當return語句中表達式的類型與函數返回值的類型不匹配時
-
常用算術轉換
整值提升(integral promotion)把字符或短整型轉換成int類型(如果
int
類型整數沒有大到足以包含所有可能的原始類型值,那么是unsigned int
)任一操作數是浮點類型的情況。
float
->double
->long double
兩個操作數均不是浮點類型。首先進行整值提升(保證沒有一個操作數是字符類型或者短整型),然后
int
->unsigned int
->long int
->unsigned long int
。有一種特殊情況,在unsigned int
和long int
相同時,如果一個操作數是unsigned int
,另一個是long int
,那么都被轉換為unsigned long int
-
當把有符號操作數和無符號操作數組合時,把有符號操作數“轉換”成無符號的值。轉換過程中需要加上或者減去
n+1
的倍數,n
是無符號類型能表示的最大值。這條規則會導致一些隱蔽的編程錯誤。例:#include <stdio.h> int main(int argc, char **argv){ unsigned int a=10; int b = -10; printf("yes, i am %d\n", b < a); printf("yes, i am %d\n", -10 < 10); return 0; }
運行結果是:
yes, i am 0 yes, i am 1
結論就是:盡量避免使用無符號整數!
-
賦值過程中的轉換
- 常用算術轉換不適用于賦值運算。C語言遵循另一條簡單的轉換規則,那就是把賦值運算符右側的表達式轉換為左側變量的類型
- 如果變量類型至少和表達式類型一樣寬,那么轉換沒有任何障礙
- 其他情況是有問題的。把浮點數賦值給整型丟掉小數部分。把某種類型的值賦給類型更狹小的變量時,如果該值在變量類型范圍之外,那么會得到無意義的結果(甚至更糟)
-
C99中的隱式轉換
- 為了定義轉換規則,C99允許每個整數類型具有“整數轉換等級”。下面是從最高到最低的排序(這里忽略了擴展的整數類型和枚舉類型)
-
long long int
、unsigned long long int
-
long int
、unsigned long int
-
int
、unsigned int
-
short int
、unsigned short int
-
char
、signed char
、unsigned char
_Bool
-
- C99用整數提升(integer promotion)取代了C89中的整值提升(integral promotion),可以將任何等級低于
int
和unsigned int
的類型轉換為int
或unsigned int
- 任一操作數是浮點類型的情況。只要兩個操作數都不是復數類型,
float
->double
->long double
- 兩個操作數均不是浮點類型。首先進行整數提升,如果兩個操作數類型相同,過程結束。否則一次嘗試以下規則,一旦遇到可應用的規則,就不再考慮其他的規則:
- 如果兩個操作數均為有符號型或無符號型,將整數轉換等級低的轉換如為等級高的操作數類型
- 如果無符號操作數的等級高于或等于有符號操作數的等級,將有符號操作數轉換為無符號操作數的類型
- 如果有符號操作數的類型可以表示無符號操作數類型的所有制,將無符號操作數轉換為有符號操作數的類型
- 否則,將兩個操作數都轉換為與有符號操作數的類型相對應的無符號類型
- 為了定義轉換規則,C99允許每個整數類型具有“整數轉換等級”。下面是從最高到最低的排序(這里忽略了擴展的整數類型和枚舉類型)
-
強制類型轉換
- 有時需要使用強制類型轉換來避免溢出,例:
long i; int j = 1000; i = j * j;//overflow may occur
在有些機器上
j*j
的值過大,無法表示為int
型,導致溢出。
可以改為i = (long)j * j;
-
sizeof
運算符- 編譯器通常就能確定
sizeof
表達式的值,C99值變長數組不可以。
- 編譯器通常就能確定