來源于公衆号“C語言與程式設計 ”,作者:薛定谔的黑白貓
典型錯誤1:指針指向
上述代碼意圖比較明顯: 定義了一個int變量a和指針變量pa,并且把a的位址給了指針pa。接着通過鍵入給a指派,但運作結果如下:
其實這個問題是我們學習指針的時候的一個典型錯誤了,我們知道調用scanf函數給變量指派時,指派對象要為位址的形式,通常是加取址符“&”,但是這裡采用的是*pa的格式,這裡涉及的指針相關知識前面給大家講過,為了更好地了解本題,就再重複一下: 對于指針來說,有己址、己值、它址、它值等特點,己址就是指針變量本身的位址,己值就是指針變量本身位址所存放的值,也就是我們通常說的指向的位址,這也正是它址,是以己值和它址意義是一樣的,而它值就是指針指向位址位置所存放的值。 而這裡的*pa表示的意義就是它值a,那就是說這麼寫的話下面兩行代碼是等價的:
scanf("%d", *pa);scanf("%d", a);
對比過後顯然是錯誤的,大家一眼看出a要寫成&a,這沒問題。但也有人說可以把*pa改成&pa,這樣行嗎? 其實這麼說的人還是對指針中己值和己址的概念沒搞清楚,&pa表示的意義是己址,即指針變量本身的位址,就是說你試圖用scanf修改指針變量本身位址上的值,而這個值原本是變量a的位址,其實就是在修改指針的指向!正确的寫法應該這樣:
scanf("%d", pa);
pa表示a的位址,即為它址,也就是&a,是以上面寫法才與下面的等價:
scanf("%d", &a);
典型錯誤2:getchar函數
char c;while((c=getchar())!=EOF){...}
這段代碼的本意是用getchar函數讀取緩沖區字元直到結束,但是在編譯運作時,發現上面幾行代碼一直報錯!邏輯上沒問題啊,那這究竟錯在哪裡?讀者可以自己思考一下再往下看。 其實産生報錯的原因有兩點,一個是對getchar函數了解不到位,另一個是EOF的問題。
我們首先來說說getchar函數的問題,标準庫中給出了該函數的使用說明:在它讀取一個字元後,會将其轉換為int類型傳回,是以首先char c要改為int c,關于getchar的問題還沒講完,後面還要說。 我們接着來看看EOF的問題,初學者對它的了解經常會有偏差,首先它是一個宏,定義于頭檔案,為-1;其次它并不是很多人了解的檔案結束符,實際上它是一個标志位,差別于其他所有字元的存在,表示一種沒有其他字元的信号。 講到這裡,我們再回到getchar函數,由上面可以看出它的傳回值必須是一個能包含所有字元的資料類型,友善它表示任意字元和EOF等标志位。 是以,上面代碼的錯誤就很明顯了,可能有兩種情況: 1.如果編譯器中的char是有符号的且EOF被定義為-1,而恰好有字元等于0xff,那麼getchar就會提前結束。當然,如果輸入全部是7位以下的字元,那很長時間不會有錯誤。 2.如果編譯器中的char是無符号的,則實際的EOF值會被截斷,不再會識别為EOF,将會陷入無限循環。 這裡肯定會有人問我們鍵入-1來模拟EOF跳出循環不行嗎?實際上是不行的,-1是有-和1兩個字元組成的,而getchar一次隻能讀取一個字元,是以上述代碼EOF與從鍵盤輸入的字元無關,那這豈不是隻能死循環了?當然不是,我們可以通過按鍵組合ctrl+d或者ctrl+z來訓示結束,當然,這裡的按鍵組合輸入隻是我們的一種約定,不應該顯示檢查按鍵組合的值。
典型錯誤3:存儲機制
char *p = NULL;p = "hello world";strcpy(p, "hello world");
題目很簡單,就問這段代碼寫的有沒有問題,如果有,問題在哪裡? 其實這個問題如果你對C語言的存儲機制非常熟悉的話,應該是很簡單的:我們簡單分析一下,第一行代碼是沒問題的,第二行意思是讓指針p指向字元串常量,單看也沒問題,而問題就出在第三行了,它的意圖是對指針p指向位址的内容進行修改,當然還用“hello world”隻是為了增加點迷惑性而已。 上面說到了C語言的記憶體機制,其實第二行代碼過後,hello world 作為字元串常量存放于記憶體中的常量區,且是隻讀,而此時指針p存放的是字元串常量的位址,第三行代碼企圖通過strcpy修改隻讀段的内容,是以很明顯會報錯,這也是這三行代碼的問題所在了。 關于C的存儲問題,可能有的人還不太了解,那就借這個機會簡單給大家提幾句,這也是以前我寫過的問題: 一個編譯的C程式占用的記憶體分為以下幾個部分:
1、棧區(stack)—也稱自動類型存儲區,由編譯器自動配置設定釋放,存放函數的參數值,局部變量的值等,例如函數調用結束後自動釋放。
2、堆區(heap)—也稱動态配置設定記憶體區,由程式員配置設定釋放,從配置設定到程式結束為止,若不釋放,程式結束時可能由OS回收,比如malloc配置設定的記憶體,free釋放的記憶體。
3、全局區(靜态區)(static)—全局變量和靜态變量的存儲是放在一塊的,初始化的全局變量和靜态變量在一塊區域,未初始化的全局變量和未初始化的靜态變量在相鄰的另一塊區域,程式結束後由系統釋放。
4、文字常量區—常量字元串放在這裡,程式結束後由系統釋放。
5、程式代碼區—編譯後的程式代碼放在這裡。 來看一個具體的C程式
怎麼樣?問題雖然簡單,但也給我們以後寫代碼提了個醒,這種不易察覺的錯誤大家一定要小心再小心,盡量避免,就說到這裡吧,感謝大家耐心閱讀! -END-