首頁 > 軟體

C語言 語意陷阱超詳細梳理總結

2022-03-25 19:01:06

1 指標與陣列

  • C語言中只有一維陣列。陣列中的元素可以是任意型別的物件,這也是多維陣列構建的理論基礎所在
  • 對於一個陣列,我們只能做兩件事:確定該陣列的大小以及獲得該陣列下標為0的元素的指標。任何一個陣列下標運算都等同於一個對應的指標運算。
  • 陣列名代表首元素的地址,無法對其進行++或者–操作,換句話說,我們無法改變陣列名(表示的值),因為陣列名是個常數,無法進行修改。

2 非陣列的指標

下面有一段程式,指出它的錯誤:

char *r;
r = malloc(strlen(s)+strlen(t));
strcpy(r,s);
strcat(r,t);
  • malloc有可能無法提供請求的記憶體,這種情況下malloc函數會通過返回一個空指標來作為「記憶體分配失敗」事件的訊號。
  • 給r分配的記憶體在使用完畢後應該及時釋放。
  • 前面的例程在呼叫malloc函數時並未分配足夠的記憶體,因為字串還包含結束標誌''。

3 作為引數的陣列宣告

1.下面列舉的兩種寫法是等價的:

char hello[] = "hello";
printf("%sn",hello);//寫法1
printf("%sn",&hello);//寫法2

原因:陣列名hello代表陣列hello首元素的地址。

2.下面的兩種寫法是等價的:

int strlen(char s[])
{
	/*具體內容*/
}
int strlen(char *s)
{
	/*具體內容*/
}

注意下面的兩種寫法:

extern char *hello;
extern char hello[];

這兩種寫法雖然是都是正確的,但是不同的形式傳遞給我們的意思卻是完全不一致的,我們要根據具體情況進行使用。

4 空指標並非空字串

注意:空指標不能對其進行解除參照。

同時注意不能出現下述寫法:

if(strcmp(p,(char*)0)==0)
	···

這種寫法是非法的,原因在於庫函數strcmp的實現中會包括一個操作,用於檢視它的指標引數所指向的內容,即對空指標進行了解除參照。

也不能出現下述寫法:

假設p是空指標

printf(p);
printf("%s",p);
//當然,這兩種寫法是等價的

這種行為是未定義的。

5 邊界計算與不對稱邊界

在我們寫回圈是最好這樣來寫:

int i = 0;
for(i = 0;i < 10; i++)
	···

這樣寫能夠更好的看出迴圈的次數,即10次。

當陣列中有10個元素時,下標的取值範圍為0到9,但是當我們不需要參照這個元素時只需要參照這個元素的地址時,我們可以這樣寫

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
for(int i = 0;&arr[i]<&(arr[10]);i++)
	···

這樣可以順利列印出陣列元素從1到10的數位,

ANSI C標準明確允許這種用法:陣列中實際不存在的"溢界"元素的地址位於陣列之外所佔記憶體之後,這個地址可以用於進行賦值和比較。當然,如果要參照該元素,那就是非法的了。對於實際去讀取這個元素的值,這種做法的結果是未定義的,而且極少有編譯器能偶檢測出這個錯誤。當然,如果試圖去修改這個元素,必然會導致程式崩潰,屬於非法存取了!

6 求值順序

C語言中只有四個運運算元(&&、||、?:和,)存在規定的求值順序。==運運算元&&和運運算元||首先對左側運算元求值,只有在需要時才對右側運算元求值。==運運算元?:有三個運算元:在a?b:c中。運算元a首先被求值,根據a的值再求運算元b或c的值(此時b或c兩個表示式根據前面a表示式的結果只會執行一個)。逗號運運算元則首先對左側運算元求值,然後"丟棄該值",再對右側運算元求值。

注意:分割函數的引數並非逗號運運算元。例如,x和y在函數f(x,y)中的求值順序是未定義的,而在函數g((x,y))中卻是確定的先x後y的循序。在後一個例子中,函數g只有一個引數。這個引數的值是這樣求得的:先對x求值,然後「丟棄」x的值,接著求y的值。

這種求值順序的存在使得某些「錯誤」的程式變為了正確,且在執行後得出正確的結果:

if(count!=0 && sum/count < smallaverage)
	···

注意:C語言中其它所有的運運算元對其運算元求值的順序是未定義的。特別是,賦值運運算元並不保證任何求值循序。

例如:下面的這中從陣列x中複製前n個元素到陣列y中的做法是不正確的,因為它對求值順序做了太多的假設:

i = 0;
while(i < n)
	y[i] = x[i++];

上面的程式碼假設y[i]的地址將在i的自增操作指向之前被求值,但這是不一定的,這依賴於編譯器的具體實現。同樣,下面的這種寫法也是不正確的:

i = 0;
while(i<n)
	y[i++] = x[i];

修改成下面這種寫法即可正常工作:

i = 0;
while(i<n)
{
	y[i] = x[i];
	i++;
}

當然,這種寫法也可以簡寫為:

for(i = 0;i < n;i++)
	y[i] = x[i];

7 整數溢位

無符號整數不會發生溢位,這是C語言所規定的,如果結果大於所能表示的最大值M,則模(M+1),也就是發生了截斷現象。

兩個有符號整數進行相加時會發生溢位,而且溢位的結果是未定義的。

下面是一種錯誤的檢查方式:

if(a + b < 0)
	complain();

因為當a+b卻是發生溢位時,所有關於結果如何假設都不再可靠。

下面是兩種正確的方式:

//方法一:
if((unsigned)a + (unsigned) > INT_MAX)
	complain();
//方法二:
if(a > INT_MAX - b)
	complain()

8 為函數提供返回值

C語言種常常通過return 返回一個值來告知作業系統的執行是成功還是失敗,典型的處理方案是。返回值為0表示程式執行成功,返回值為非0則表示程式執行失敗。我們常常會在程式的末尾加上return 0操作。

到此這篇關於C語言 語意陷阱超詳細梳理總結的文章就介紹到這了,更多相關C語言 語意陷阱內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com