C陷阱篇之常見手誤

2021-06-19 17:53:15 字數 1848 閱讀 7670

c的某些語法容易讓人不小心觸雷,比如

從0開始的下標

很多高階語言中,定義n個元素的陣列,下標範圍是從1到n,但c特殊,n元素的c陣列中沒有下標為n的元素,只有從0到n-1的下標。所以使用c陣列時不要犯這種錯誤:

int i, a[10];

for ( i = 1; i <= 10 ; i++ )    //i=10時超出陣列邊界

八進位制or十進位制常數

c編譯器會把數字025當作八進位制,等於十進位制的21,因此在使用常整數時不要認為多個0無關緊要,特別在從其他檔案或標準中拷貝一些常係數陣列時,要留意預防。

未初始化變數

變數未初始化往往帶來很難查詢的隨機錯誤,下面是開發中真實的例子:

typedef struct tag_parmparm;

/*結構體初始化*/

parm spliterparm;

spliterparm.clip = &ifilename;

spliterparm.initparsingenable = iparsingmode;

/*呼叫*/

res = open(&spliterparm);

ishttpstream是後期新增的乙個成員變數,初始化部分忘記給它賦值,而在open函式裡有:

這就導致open隨機執行到不同分支。變數未初始化的主要表現就是多次執行同樣程式,隨機錯誤。未初始化的問題很常見,特別是結構體,其定義和初始化不象普通區域性變數那樣相距很近,所以標頭檔案裡成員變數改變後很容易遺漏對應的初始化。

浮點數比較和交換

void main()

else  }

結果是error。浮點數是為了能用二進位制表達小數而引入的一種量化形式,轉換時存在捨入誤差,因此不能直接用==比較浮點數。正確做法是確定乙個誤差範圍,通過比較兩浮點數差值是否落在這個誤差範圍內來判定是否相等,即:

const float delta = 0.1

void main()

else  }

同樣,由於浮點數有捨入誤差,也不滿足交換定律,如浮點數float x=1/3,y=1/6,z=1/7;

和整數不同,x*y/z不等於x*(y/z)。

字串結尾的隱含結束符導致記憶體越界

char *r = malloc(strlen(s) + strlen(t));

strcpy(r, s);

strcat(r, t);

以上實現忘記了字串結尾隱含的空字元,strlen()返回字串裡真正的字元數,但不包括結尾的空字元。因此如果strlen(s)是n,則s需要n+1個位元組空間儲存。即:

char *r = malloc(strlen(s) + strlen(t) + 1);

strcpy(r, s);

strcat(r, t);

log語句中的有效操作

有人認為printf/log/trace等列印函式的唯一功能就是輸出調式資訊,因而隨意對待它們,但是printf等函式裡也能包含有效操作,如:

int a[5]=;

int *ptr = a;

printf(「%d」, *(++ptr));

*(ptr++) += 123;

單純把printf看做log語句,修改程式時就可能隨手注釋掉(printf開開關關經常發生),而printf內的有效操作也同時被刪除,類似情形還包括printf內的函式呼叫等。因此最好把printf和有效操作放在兩條語句,否則是自己挖坑。

C陷阱篇之常見手誤

c的某些語法容易讓人不小心觸雷 很多高階語言中,定義n個元素的陣列,下標範圍是從1到n,但c特殊,n元素的c陣列中沒有下標為n的元素,只有從0到n 1的下標。所以使用c陣列時不要犯這種錯誤 int i,a 10 for i 1 i 10 i i 10時超出陣列邊界 八進位制or十進位制常數 c編譯器...

C 常見陷阱之 語法

至少在2018的今天,c 的函式引數求值順序仍然是未定的 交給編譯器處理 所以函式引數求值的順序可能在某些情況下回導致一些問題。看下面這個例子 int test 5 printf d,d,d n test,test,test test 5 printf d,d,d n test test,test ...

C 常見陷阱

注 char型別在標準中是個特別的存在,它沒有被規定為有符號或無符號。比如int指的是有符號,而char不一樣。在程式設計時最好給char寫上符號,否則同樣的表示式可能在不同的編譯器 平台會有不同的結果。請觀察乙個程式輸出 int main 輸出 fffffff1,fffffff2,f0f2 fff...