程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 必須弄懂的495個C語言問題

必須弄懂的495個C語言問題

編輯:關於C語言

1.1 我如何決定使用那種整數類型?


如果需要大數 值(大於32, 767 或小於¡32, 767), 使用long 型。否則, 如果空間很重要(如有大數組或很多結構), 使用short 型。除此之外, 就使用int 型。如果嚴格定義的溢出特征很重要而負值無關緊要, 或者你希望在操作二進制位和字節時避免符號擴展的問題, 請使用對應的無符號類型。但是, 要注意在表達式中混用有符號和無符號值的情況。


盡管字符類型(尤其是無符號字符型) 可以當成“小” 整型使用, 但由於不可預知的符號擴展和代碼增大有時這樣做可能得不償失。使用無符號字符型有所幫助;類似的問題參見問題12.1。


在選擇浮點型和雙精度浮點型時也有類似的權衡。但如果一個變量的指針必須為特定的類型時, 以上規則不再適用。


如果因為某種原因你需要聲明一個有嚴格大小的變量, 確保象C99 的<inttypes.h> 那樣用某種適當的typedef 封裝這種選擇。通常, 這樣做唯一的好原因是試圖符合某種外部強加的存儲方案, 請參見問題20.3。


如果你需要操作超過C 的內置類型支持的超大變量, 請參見問題18.17。


參 考資料: [K&R1, Sec. 2.2 p. 34]; [K&R2, Sec. 2.2 p. 36, Sec. A4.2 pp.195-6, Sec. B11 p. 257]; [ISO, Sec. 5.2.4.2.1, Sec. 6.1.2.5]; [H&S, Secs. 5.1,5.2 pp.110-114]。


1.2 64 位機上的64 位類型是什麼樣的?

C99 標准定義了long long 類型, 其長度可以保證至少64 位, 這種類型在某些編譯器上實現已經頗有時日了。其它的編譯器則實現了類似longlong 的擴展。另一方面, 也可以實現16 位的短整型、32 位的整型和64 位的長整型, 有些編譯器正是這樣做的。

 

參見問題18.17。


參考資料: [C9X, Sec. 5.2.4.2.1, Sec. 6.1.2.5]


1.3 怎樣定義和聲明全局變量和函數最好?


首 先, 盡管一個全局變量或函數可以(在多個編譯單元中) 有多處“聲明”, 但是“定義” 卻只能允許出現一次。定義是分配空間並賦初值(如果有) 的聲明。最好的安排是在某個相關的.c 文件中定義, 然後在頭文件(.h) 中進行外部聲明, 在需要使用的時候, 只要包含對應的頭文件即可。定義變量的.c 文件也應該包含該頭文件, 以便編譯器檢查定義和聲明的一致性。


這條規則提供了高度的 可移植性: 它和ANSI C 標准一致, 同時也兼容大多數ANSI 前的編譯器和連接器。Unix 編譯器和連接器通常使用“通用模式” 允許多重定義, 只要保證最多對一處進行初始化就可以了; ANSI C 標准稱這種行為為“公共擴展”, 沒有語帶雙關的意思。


可以使用預處理技巧來使類似DEFINE(int, i);的語句在一個頭文件中只出現一次, 然後根據某個宏的設定在需要的時候轉化成定義或聲明。但不清楚這樣的麻煩是否值得。


如果希望讓編譯器檢查聲明的一致性, 一定要把全局聲明放到頭文件中。特別是, 永遠不要把外部函數的原型放到.c 文件中: 通常它與定義的一致性不能得到檢查, 而矛盾的原型比不用還糟糕。


參見問題10.4 和18.6。


參 考資料: [K&R1, Sec. 4.5 pp. 76-7]; [K&R2, Sec. 4.4 pp. 80-1]; [ISO, Sec.6.1.2.2, Sec. 6.7, Sec. 6.7.2, Sec. G.5.11]; [Rationale, Sec. 3.1.2.2]; [H&S, Sec. 4.8pp. 101-104, Sec. 9.2.3 p. 267]; [CT&P, Sec. 4.2 pp. 54-56].


1.4 extern 在函數聲明中是什麼意思?

 

它可以用作一種格式上的提示表明函數的定義可能在另一個源文件中, 但在extern int f();和int f();之間並沒有實質的區別。


參考資料: [ISO, Sec. 6.1.2.2, Sec. 6.5.1]; [Rationale, Sec. 3.1.2.2]; [H&S,Secs. 4.3,4.3.1 pp. 75-6].


1.5 關鍵字auto 到底有什麼用途?


毫無用途;它已經過時。參見問題20.32。


參考資料: [K&R1, Sec. A8.1 p. 193]; [ISO, Sec. 6.1.2.4, Sec. 6.5.1;]; [H&S,Sec. 4.3 p. 75, Sec. 4.3.1 p. 76].


1.6 我似乎不能成功定義一個鏈表。我試過typedef struct { char*item; NODEPTR next; } *NODEPTR; 但是編譯器報了錯誤信息。難道在C語言中一個結構不能包含指向自己的指針嗎?


C 語言中的結構當然可以包含指向自己的指針; [K&R2, 第6.5 節] 的討論和例子表明了這點。NODEPTR 例子的問題是在聲明next 域的時候typedef 還沒有定義。為了解決這個問題, 首先賦予這個結構一個標簽(“struct node”)。然後,聲明“next” 域為“struct node *”, 或者分開typedef 定義和結構定義, 或者兩者都采納。以下是一個修改後的版本:
struct node {
char *item;
struct node *next;
};
typedef struct node *NODEPTR;


至少還有三種同樣正確的方法解決這個問題。


在用typedef 定義互相引用的兩個結構時也會產生類似的問題, 可以用同樣的方法解決。


參見問題2.1。


參考資料: [K&R1, Sec. 6.5 p. 101]; [K&R2, Sec. 6.5 p. 139]; [ISO, Sec.6.5.2, Sec. 6.5.2.3]; [H&S, Sec. 5.6.1 pp. 132-3]。


1.7 怎樣建立和理解非常復雜的聲明?例如定義一個包含N 個指向返回指向字符的指針的函數的指針的數組?


這個問題至少有以下3 種答案:


1. char *(*(*a[N])())();


2. 用typedef 逐步完成聲明:
typedef char *pc; /* 字符指針*/
typedef pc fpc(); /* 返回字符指針的函數*/
typedef fpc *pfpc; /* 上面函數的指針*/
typedef pfpc fpfpc(); /* 返回函數指針的函數*/
typedef fpfpc *pfpfpc; /* 上面函數的指針*/
pfpfpc a[N]; /* 上面指針的數組*/


3. 使用cdecl 程序, 它可以把英文翻譯成C 或者把C 翻譯成英文:
cdecl> declare a as array of pointer to function returning

pointer to function returning pointer to char

char *(*(*a[])())()
通過類型轉換, cdecl 也可以用於解釋復雜的聲明, 指出參數應該進入哪一對括號(如同在上述的復雜函數定義中)。參見問題18.1。

一本好的C 語言書都會解釋如何“從內到外” 解釋和理解這樣復雜的C 語言聲明(“模擬聲明使用”)。


上文的例子中的函數指針聲明還沒有包括參數類型信息。如果參數有復雜類型, 聲明就會變得真正的混亂了。現代的cdecl 版本可以提供幫助。

 

參考資料: [K&R2, Sec. 5.12 p. 122]; [ISO, Sec. 6.5ff (esp. Sec. 6.5.4)]; [H&S,Sec. 4.5 pp. 85-92, Sec. 5.10.1 pp. 149-50]。


1.8 函數只定義了一次, 調用了一次, 但編譯器提示非法重定義了。


在 范圍內沒有聲明就調用(可能是第一次調用在函數的定義之前) 的函數被認為返回整型(int) (且沒有任何參數類型信息), 如果函數在後邊聲明或定義成其它類型就會導致矛盾。所有函數(非整型函數一定要) 必須在調用之前聲明。另一個可能的原因是該函數與某個頭文件中聲明的另一個函數同名。


參見問題11.4 和15.1


參考資料: [K&R1, Sec. 4.2 p. 70]; [K&R2, Sec. 4.2 p. 72]; [ISO, Sec. 6.3.2.2];[H&S, Sec. 4.7 p. 101].


1.9 main() 的正確定義是什麼? void main() 正確嗎?


參見問題11.11 到11.16。(這樣的定義不正確)。


1.10 對於沒有初始化的變量的初始值可以作怎樣的假定?如果一個全局變量初始值為“零”, 它可否作為空指針或浮點零?


具 有“靜態” 生存期的未初始化變量(即, 在函數外聲明的變量和有靜態存儲類型的變量) 可以確保初始值為零, 就像程序員鍵入了“=0” 一樣。因此, 這些變量如果是指針會被初始化為正確的空指針, 如果是浮點數會被初始化為0.0 (或正確的類型, 參見第5 章)。


具有“自動” 生存期的變量(即, 沒有靜態存儲類型的局部變量) 如果沒有顯示地初始化, 則包含的是垃圾內容。對垃圾內容不能作任何有用的假設。這些規則也適用於數組和結構(稱為“聚合體” ); 對於初始化來說, 數組和結構都被認為是“變量”。


用malloc() 和realloc() 動態分配的內存也可能包含垃圾數據, 因此必須由調用者正確地初始化。用calloc() 獲得的內存為全零, 但這對指針和浮點值不一定有用(參見問題7.26 和第5 章)。


參 考資料: [K&R1, Sec. 4.9 pp. 82-4]; [K&R2, Sec. 4.9 pp. 85-86]; [ISO, Sec.6.5.7, Sec. 7.10.3.1, Sec. 7.10.5.3]; [H&S, Sec. 4.2.8 pp. 72-3, Sec. 4.6 pp. 92-3,Sec. 4.6.2 pp. 94-5, Sec. 4.6.3 p. 96, Sec. 16.1 p. 386.]。

1.11 代碼int f() { char a[] = "Hello, world!";} 不能編譯。


可能你使用的是ANSI 之前的編譯器, 還不支持“自動聚集”(automatic aggregates,即非靜態局部數組、結構和聯合) 的初始化。參見問題11.28。


1.12 這樣的初始化有什麼問題?char *p = malloc(10); 編譯器提示“非法初始式” 雲雲。


這個聲明是靜態或非局部變量嗎?函數調用只能出現在自動變量(即局部非靜態變量) 的初始式中。


1.13 以下的初始化有什麼區別?char a[] = "string literal"; char *p= "string literal"; 當我向p[i] 賦值的時候, 我的程序崩潰了。


字 符串常量有兩種稍有區別的用法。用作數組初始值(如同在char a[] 的聲明中), 它指明該數組中字符的初始值。其它情況下, 它會轉化為一個無名的靜態字符數組, 可能會存儲在只讀內存中, 這就是造成它不一定能被修改。在表達式環境中, 數組通常被立即轉化為一個指針(參見第6 章), 因此第二個聲明把p 初始化成指向無名數組的第一個元素。
為了編譯舊代碼, 有的編譯器有一個控制字符串是否可寫的開關。


參見問題1.11、6.1、6.2 和6.6。


參考資料: [K&R2, Sec. 5.5 p. 104]; [ISO, Sec. 6.1.4, Sec. 6.5.7]; [Rationale,Sec. 3.1.4]; [H&S, Sec. 2.7.4 pp. 31-2]。


1.14 我總算弄清除函數指針的聲明方法了, 但怎樣才能初始化呢?


用下面這樣的代碼
extern int func();
int (*fp)() = func;


當一個函數名出現在這樣的表達式中時, 它就會“蛻變” 成一個指針(即, 隱式地取出了它的地址), 這有點類似數組名的行為。


通常函數的顯示聲明需要事先知道(也許在一個頭文件中)。因為此處並沒有隱式的外部函數聲明(初始式中函數名並非一個函數調用的一部分)。


參見問題1.8 和4.8。

 

更詳細的”必須弄懂的495個C語言問題“下載地址http://share.eepw.com.cn/share/download/id/89420

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved