學習過c/c++的程式設計師知道,在一開始就要對它的基本資料型別瞭如指掌。像int, unsigned int, long, unsigned long。。。。。。目的就是明白我們可以用哪些資料型別來表現我們的演算法。就是因為太基礎了,所以有時候卻不免在細節上忽略了它們的特點,造成了比較隱秘的錯誤。
就以最熟悉的int和unsigned int來說明這個情況。在x86平台上s32表示是乙個帶符號的32位整數,u32表示的是乙個無符號的32位整數。所以它們的區別在於能夠表示的數的範圍不同:前者能夠表示負整數,而後者只能表示非負整數。
現在有這麼乙個函式:
void func1( unsigned int startpos, unsigned int len, void* data );
它的作用是以data為基位址,向後偏移startpos單位開始的len個單位的資料傳送到目標。在這裡,假設目標是個庫函式,並且它能夠接受的資料是有限的,大小為0xffff。根據要求,func1可能的實現是:
assert( startpos >= 0 );
assert( startpos + len < 0xffff );
libfunc( startpos, len, data);
以上的**先是根據要求,簡單的用assert()來對引數進行檢查,然後就傳送給庫函式。這是一般的做法,但是這些**存在一些不足,在某些情況下會導致程式不能正常執行。
1. 首先是第一行的**其實並不能起到檢查的作用。這是因為startpos的型別是unsigned int, 那麼它的值就是非負的,所以這句語句的返回永遠是真。相反地,假如型別是int,那麼就能夠起到很好的保衛作用。關於這一點,絕大多數的程式設計師稍加思考就應該很快明白。
2. 其次第二行**同樣存在著同第一行相同的情況。這次可能絕大多數的人不會在第一時間明白其中的奧妙。因為startpos和len都是非負數,而要求是總長不能超過0xffff——也是個非負數(編譯器會把0xffff翻譯成乙個unsigned int,讓它與不等號左式相對應)。那麼看起來都很好,確實能夠起到保護作用的?
其實問題不在於第二行本身,而在於基本資料型別的特性。假如這樣呼叫上面的那個函式:
int st = -6, len = 8;
func1( st, len, addr );
那麼語句要表達的就是:把addr所指向的位址,向前偏移6個單位作為起始位址,連續8個單位的資料傳送給庫函式。顯然這是不符合函式設計的初衷的。但是函式還是會毫無提示資訊地接受這些引數,並傳送給庫函式。這是因為編譯器在處理int和unsigned int的轉換時候,只是簡單地把資料變數的內容傳輸到另外乙個。像前面的例子,就是把st的內容直接傳給作為引數的startpos。而-6的表示是0xfffffffffffffffa ,8的表示還是0x8,它們相加的結果是0x2。這是因為當無符號數相加溢位之後,只保留合法的部分。所以這些引數順利地通過了兩行保護**的檢測,而發給了庫函式。那麼結果就會是庫函式會試圖訪問 addr+0xfffffffffffffffa的位址開始的8個單位的資料,這樣就會出現非法訪問的錯誤。
簡簡單單的基本資料型別的使用,背後卻包含著數碼表示,編譯方式和指令實現那麼多的技術要點。所以在選擇函式的資料型別時候,一定要考慮周全。並且要盡可能檢測出非正常的使用方式,並用合理的保護**來檢查出這些潛在的問題。
C C 基本資料型別
學了c然後c 然後mfc windows,然後是c 其中資料型別很多,由基本型別衍生的typedef型別也n多。熟知基本資料型別是我們正確表達實際問題中各種資料的前提,因此我分類總結了一下c c windows c 基本資料型別,以便日後查閱。ansi c c 基本資料型別 說明 1 型別修飾符si...
C C 基本資料型別
學了c然後 c 然後 mfc windows,然後是c 其中資料型別很多,由基本型別衍生的 typedef 型別也n 多。熟知基本資料型別是我們正確表達實際問題中各種資料的前提,因此我分類總結了一下 c c windows c 基本資料型別,以便日後查閱。ansi c c 基本資料型別 type s...
c c 基本資料型別
整型 浮點型 int main 執行結果 size char 1 short int 2 int 4 long 4 long long int 8 int long long 8 long long 8 三種寫法相同 float 4 double 8型別 大小 byte char 1short 2i...