前文回顧:C/C++編程新手錯誤語錄 錯誤語錄(續一)
(13)“整型變量僅僅意味著一個整數”
<!-- frame contents -->
<!-- /frame contents -->
當我們還是一個新手,看整型就是整數;
當我們成為高手,看什麼都是整型。
整型,在所有C/C++基本數據類型中最富有藝術魅力和奇幻色彩。
我們從某聞名論壇的一篇帖子開始一窺整型的奧妙。
問:Vxworks操作系統啟動一個任務的函數是taskSpawn(char* name, int priority, int options, int stacksize, FUNCPTR function, int arg1,.. , int arg10),它只接受整型參數,我該怎麼辦才能給它傳一個結構體(在32位PowerPC平台下)?
答:可以傳入結構體的指針,在32位PowerPC平台下,指針本質上就是一個32位整數,在函數體內將整型強制轉化為結構體指針就可訪問結構體的每一個元素。
如:
//啟動任務1
taskSpawn(“task1”, 180, NULL, 10000, Task1Fun, &pStrUCtAr,0,0,0,0,0,0,0,0,0);
//task1函數
Task1Fun ( int arg1 )
{
struct_x * pStructx = (struct_x *) arg1; //將整型強制轉化為結構體指針
…
}
在此提出“泛整型”的概念,(unsigned)char、(unsigned)short int、(unsigned)int、(unsigned)long int等都屬於這個范疇,指針必然屬於“泛整型”的范圍。用指針的高超境界,也為將其看做一個“泛整型”。
看看軟件的具體設計文檔,其數據結構定義部分經常看到“INT8、UINT8、INT16、UINT16、INT32、UINT32、INT64、UINT64”或“BYTE、Word、DWORD”等數據類型,它們在本質上都是(unsigned)char、(unsigned)short int、(unsigned)int、(unsigned)long int宏定義的結果,都屬於“泛整型”。所以,“泛整型”的概念真實地體現在日常的軟件設計當中。
更多內容請看C/C++技術專題 Java編程開發手冊專題,或
正因為各種指針類型在本質上都是“泛整型”,因此它們可以互相轉化:
int a, b;
memset( (char*) &a, (char*) &b, sizeof(int) );
等價於:
int a, b;
a = b;
<!-- frame contents -->
<!-- /frame contents -->
從來沒有人會用memset( (char*) &a, (char*) &b, sizeof(int) )來代替a = b,這裡只是為了說明問題。下面的代碼則經常用到:
int *p = (int *) malloc(100*sizeof(int));
memset ( p, 0, 100*sizeof(int) ); //將申請的內存空間清0
我們看memset的函數原型為:
void * memset ( void * buffer, int c, size_t num );
實際上它接受的第一個參數是無類型指針,在memset函數體內,其它任意類型的指針都向void *轉化了。類似的內存操作函數memcpy所接受的源和目的內存地址也是無類型指針。
char *轉化為int *後的值雖然不變(還是那個地址),但是其++、--等操作的含義卻發生了變化,這也是要注重的。
char *p;
++p;
與
char *p;
++(int *)p;
的結果是不一樣的,前者的p值加了1,而後者的則增加了sizeof(int)。
下面來剝Windows程序設計中消息傳遞函數兩個參數的皮,看看它們究竟是什麼:
typedef UINT WPARAM;
typedef LONG LPARAM;
原來,WPARAM和LPARAM其實都屬於“泛整型”,所以不要報怨消息處理函數只能接受“泛整型”。實際上,從指針的角度上來講,在C/C++中,可以獲得任何類型實例(變量、結構、類)的指針,所以Windows的消息處理函數實際上可以接受一切類型的參數。
驚天動地一句話:“泛整型”可表征一切。
更多內容請看C/C++技術專題 Java編程開發手冊專題,或
(14)“值傳遞一定不會改變參數”
理論而言,值傳遞的確不會改變參數的內容。但是,某年某月的某一天,隔壁Office的碩士mm寫了這麼一段程序,參數的值卻被改變了:
int n = 9;
char a[10];
<!-- frame contents -->
<!-- /frame contents -->
example ( n, a ); //調用函數example(int n,char *pStr)
printf (“%d”, n ); //輸出結果不是9
大概整個office的人都被搞懵了,都說編譯器瞎搞,有問題。找到筆者,筆者憑借以往的經常,一眼就看出來不是什麼編譯器出錯,而是在函數example內對字符串a的訪問越界!
當在函數example內對a的訪問越界後,再進行寫操作時,就有可能操作到了n所在的內存空間,於是改變了n的值。
給出這個語錄,並非為了推翻“值傳遞不會改變參數”的結論,而是為了從側面證實在C/C++語言中,數組越界是多麼危險的錯誤!
下面的兩個函數有明顯的數組越界:
void example1()
{
char string[10];
char* str1 = "0123456789";
strcpy( string, str1 );
}
void example 2(char* str1)
{
char string[10];
if( strlen( str1 ) <= 10 )
{
strcpy( string, str1 );
}
}
而這個函數的越界就不這麼明顯:
void example3()
{
char string[10], str1[10];
int i;
for(i=0; i<10; i++)
{
str1[i] = 'a';
}
strcpy( string, str1 );
}
其實,這個函數危險到了極點。因為對於strcpy函數而言,拷貝的時候要碰到’