天天看點

讀書摘要——《C FAQ》

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

    C99 标準定義了long long 類型,其長度可以保證至少64 位。

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

    最好是在某個相關的.c 檔案中定義,然後在頭檔案(.h) 中進行外部聲明在需要使用的時候,隻要包含對應的頭檔案即可。定義該變量的.c 檔案也應該包含該頭檔案,以便編譯器檢查定義和聲明的一緻性。

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

    毫無用途!它已經過時。

2.7    怎樣建立和了解非常複雜的聲明?例如定義一個包含N 個指向傳回指向字元的指針的函數的指針的數組?

    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[])())()

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

    這個聲明是靜态或非局部變量嗎?函數調用隻能用于自動變量(即局部非靜态變量) 的初始化表達式中。

2.13    以下的初始化有什麼差別?char a[] = "string literal"; char *p= "string literal"; 當我向p[i] 指派的時候,我的程式崩潰了。

     字元串常量有兩種稍有差別的用法。用作數組初始值(如同在char a[] 的聲明中),它指明該數組中字元的初始值。其它情況下 它會轉化為一個無名的靜态字元數組,可能會存儲在隻讀記憶體中,這就造成它不一定能被修改。在表達式環境中,數組通常被立即轉化為一個指針,是以第二個聲明 把p初始化成指向無名數組的第一個元素。

3.1    聲明struct x1 { . . . }; 和typedef struct { . . . } x2; 有什麼不同?

    第一種形式聲明了一個"結構标簽"; 第二種聲明了一個"類型定義"。主要的差別是在後文中你需要用"struct x1" 引用第一種,而用"x2" 引用第二種。也就是說第二種聲明更像一種抽象類型——使用者不必知道它是一個結構,而在聲明它的執行個體時也不需要使用struct 關鍵字。

3.2    為什麼struct x { . . . }; x thestruct; 不對?

    C 不是C++。結構标簽不能自動生成類型。

3.6    我遇到這樣聲明結構的代碼: struct name { int namelen; charnamestr[1];}; 然後又使用一些記憶體配置設定技巧使namestr 數組用起來好像有多個元素。這樣合法和可移植嗎?

    這種技術十分普遍,盡管Dennis Ritchie 稱之為"和C實作的無保證的親密接觸"。官方的解釋認定它沒有嚴格遵守C 标準,盡管它看來在所有的實作中都可以工作。仔細檢查數組邊界的編譯器可能會發出警告。

    另一種可能是把變長的元素尺寸聲明得很大,而不是很小; 在上例中:

    ...

    char namestr[MAXSIZE];

    MAXSIZE 比任何可能存儲的name 值都大。但是,這種技術似乎也不完全符合标準的嚴格解釋。這些"親密" 結構都必須小心使用,因為隻有程式員知道它的大小,而編譯器卻一無所知。

    C99 引入了"靈活數組域" (flexible array member),允許結構的最後一個域省略數組大小,為類似問題提供了一個圓滿的解決方案。

3.9    怎樣從/向資料檔案讀/寫結構?

    用fwrite() 寫一個結構相對簡單:fwrite(&somestruct,sizeof somestruct,1,fp);對應的fread() 調用可以再把它讀回來。但是這樣檔案讀寫卻不具備良好的可移植性。

    移植性更好的方案是寫一對函數,用可移植(可能甚至是人可讀) 的方式按域讀寫結構,盡管開始可能工作量稍大。

3.12    如何确定域在結構中的位元組偏移?

    ANSI C 在<stddef.h> 中定義了offsetof() 宏,用offsetof(struct s,f) 可以計算出域f在結構s中的偏移量。如果出于某種原因,你需要自己實作這個功能,可以使用下邊這樣的代碼:

    #define offsetof(type,f) ((size_t) /

    ((char *)&((type *)0)->f - (char *)(type *)0))

4.1    為什麼這樣的代碼: a[i] = i++; 不能工作?

    子表達式i++ 有一個副作用——它會改變i 的值。由于i 在同一表達式的其它地方被引用,這會導緻無定義的結果,無從判斷該引用(左邊的a[i] 中)是舊值還是新值。注意,盡管在K&R 中建議這類表達式的行為不确定,但C 标準卻強烈聲明它是無定義的。

4.5    我可否用括号來強制執行我所需要的計算順序?

    一般來講,不行。運算符優先級和括弧隻能賦予表達是計算部分的順序. 在如下的代碼中:f() + g() * h(); 盡管我們知道乘法運算在加法之前,但這并不能說明這三個函數哪個會被首先調用。

4.6    可是&& 和|| 運算符呢?我看到過類似while((c = getchar()) != EOF && c != ’/n’) 的代碼

    這些運算符在此處有一個特殊的"短路"例外:如果左邊的子表達式決定最終結果(即真對于|| 和 假對于&& ) ,則右邊的子表達式不會計算。是以,從左至右的計算可以確定。逗号表達式也是如此。

5.2    *p++ 自增p 還是p 所指向的變量?

    字尾++ 和-- 操作符本質上比字首一目操作的優先級高,是以*p++ 和*(p++) 等價,它自增p并傳回p 自增之前所指向的值。

5.3    我有一個char * 型指針正巧指向一些int 型變量,我想跳過它們。為什麼如下的代碼((int *)p)++; 不行?

    在C 語言中,類型轉換意味着"把這些二進制位看作另一種類型,并作相應的對待";這是一個轉換操作符,根據定義它隻能生成一個右值(rvalue)。而右值既不能被指派,也不能用++進行自增。

5.5    我能否用void** 指針作為參數,使函數按引用接受一般指針?

    不可移植。C 中沒有一般的指針的指針類型。void* 可以用作一般指針隻是因為當它和其它類型互相指派的時候,如果需要,它可以自動轉換成其它類型; 但是,如果試圖這樣轉換所指類型為void* 之外的類型的void** 指針時,這個轉換不能完成。

5.7    C 有"按引用傳遞" 嗎?

    真的沒有。

    嚴格地講,C 總是按值傳遞。你可以自己模拟按引用傳遞,定義接受指針的函數,然後在調用時使用& 操作符。事實上,當你向函數傳入數組 時,編譯器本質上就是在模拟按引用傳遞。但是C 沒有任何真正等同于正式的按引用傳遞或C++ 的引用參數的東西。另一方面,類似函數的預處理宏可以提供一種"按名稱傳遞"的形式。

6.3    用縮寫的指針比較 ``if(p)" 檢查空指針是否可靠?如果空指針的内部表達不是 0 會怎麼樣?

當 C 在表達式中要求布爾值時, 如果表達式等于 0 則認為該值為假, 否則為真。換言之, 隻要寫出

    if(expr)

無論 ``expr" 是任何表達式, 編譯器本質上都會把它當

    if((expr) != 0)

處理。 

    是以,這個問題的答案是:可以!兩者之間不存在差别

6.4    NULL是什麼,它是怎麼定義的?

    作為一種風格,很多人不願意在程式中到處出 現未加修飾的0。是以定義了預處理宏NULL (在<stdio.h> 和其它幾個頭檔案中) 為空指針常數,通常是0 或者((void *)0) 。希望差別整數0 和空指針0 的人可以在需要空指針的地方使用NULL。

    NULL 隻是一種風格習慣;預處理器把所有的NULL 都還原回0。

6.5     在使用非全零作為空指針内部表達的機器上,NULL 是如何定義的?

    跟其它機器一樣:定義為0或某種形式的0。 當程式員請求一個空指針時,無論寫"0" 還是"NULL",都是由編譯器來生成适合機器的空指針的二進制表達形式。是以,在空指針的内部表達不為0 的機器上定義NULL 為0,跟在其它機器上一樣合法:編譯器在指針上下文看到的未加修飾的0 都會被生成正确的空指針。

21。NULL 定義成#define NULL ((char *)0) 難道不就可以向函數傳入不加轉換的NULL 了嗎?

    一般情況下,不行。複雜之處在于,有的機器不同類型資料的指針有不同的内部表達。這樣的NULL 定義對于接受字元指針的的函數沒有問題,但對于其它類型的指針參數仍然有問題(在缺少原型的情況下),而合法的構造如 FILE *fp = NULL;則會失敗。

    不過,ANSI C 允許NULL 的可標明義 #define NULL ((void *)0)

6.7    如果NULL 和0 作為空指針常數是等價的,那我到底該用哪一個呢?

    C 程式員應該明白,在指針上下文中NULL 和0 是完全等價的,未加修飾的0 也完全可以接受。任何使用NULL (跟0 相對) 的地方都應該看作一種溫和的提示,是在使用指針; 程式員和編譯器都不能依靠它來差別指針0 和整數0。

6.8    但是如果NULL 的值改變了,比如在使用非零内部空指針的機器上,難道用NULL (而不是0) 不是更好嗎?

    不。用NULL 可能更好,但不是這個原因。盡管符号常量經常代替數字使用以備數字的改變,但這不是用NULL 代替0 的原因。語言本身確定了源碼中的0 (用于指針上下文) 會生成空指針。NULL 隻是用作一種格式習慣。

6.10    這有點奇怪。NULL 可以確定是0,但空(null) 指針卻不一定?

    随便使用術語"null" 或"NULL" 時,可能意味着以下一種或幾種含義:

    1.    概念上的空指針,抽象語言概念。它使用以下的東西實作。

    2.    空指針的内部(或運作期) 表達形式,這可能并不是全零,而且對不用的指針類型可能不一樣。真正的值隻有編譯器開發者才關心。C 程式的作者永遠看不到它們,因為他們使用。。。

    3.    空指針常數,這是一個常整數0 。

    4.    NULL 宏,它被定義為0 。

    5.    ASCII 空字元(NUL),它的确是全零,但它和空指針除了在名稱上以外,沒有任何必然關系。

    6.    "空串" (null string),它是内容為空的字元串("")。在C 中使用空串這個術語可能令人困惑,因為空串包括空字元(’/0’),但不包括空指針。

    本文用短語"空指針" ("null pointer",小寫) 表示第一種含義,辨別"0" 或短語"空指針常數" 表示含義3,用大寫NULL 表示含義4。

7.1    我在一個源檔案中定義了char a[6],在另一個中聲明了extern char *a 。為什麼不行?

    你在一個源檔案中定義了一個字元串,而在另一個檔案中定義了指向字元的指針。extern char * 的聲明和真正的定義無法比對。類型T 的指針和類型T的數組并非同種類型。

7.3    那麼,在C 語言中"指針和數組等價" 到底是什麼意思?

    在C 語言中對數組和指針的困惑多數都來自這句話。說數組和指針"等價"不表示它們相同,或者能夠互換。它的意思是說數組和指針的運算法則允許使用指針友善的通路數組或者模拟數組。

    特别需要注意的是,等價的基礎來自這個關鍵定義:

    一個類型T的數組出現在表達式中,會蛻變為一個指向數組第一個成員的指針,除了三種例外情況。

    這就是說,一旦數組出現在表達式中,編譯器會隐式地生成一個指向數組第一個成員地指針,就像程式員寫出了&a[0] 一樣。

    例外的情況是,數組為sizeof 或&操作符的操作數,或者為字元數組的字元串初始值。

    作為這個這個定義的後果,編譯器并那麼不嚴格區分數組下标操作符和指針。

7.8    我遇到一些"搞笑" 的代碼,包含5["abcdef"] 這樣的"表達式"。這為什麼是合法的C 表達式呢?

    是的,數組和下标在C 語言中可以互換。這個奇怪的事實來自數組下标的指針定義,即對于任何兩個表達式a 和e,隻要其中一個是指針表達式而另一個為整數,則a[e] 和*((a)+(e)) 完全一樣。這種交換性在許多C 語言的書中被看作值得驕傲的東西,但是它除了在混亂C 語言競賽之外,其實鮮有用武之地。

7.9    既然數組引用會蛻化為指針,如果arr 是數組,那麼arr 和&arr 又有什麼差別呢?

    差別在于類型。

    在标準C 中,&arr 生成一個"T 型數組" 的指針,指向整個數組。

    在所有的C 編譯器中,對數組的簡單引用(不包括& 操作符)生成一個類型為T的指針,指向數組的第一進制素。

7.10    我如何聲明一個數組指針?

    通常,你不需要。當人們随便提到數組指針的時候,他們通常想的是指向它的第一個元素的指針。

    如果你真的需要聲明指向整個數組的指針,使用類似"int (*ap)[N];" 這樣的聲明。其中N 是數組的大小

7.15    當我向一個接受指針的指針的函數傳入二維數組的時候,編譯器報錯了。

    數組蛻化為指針的規則不能遞歸應用。數組的數組(即C 語言中的二維數組) 蛻化為數組的指針,而不是指針的指針。

    如果一個函數已經定義為接受指針的指針,那麼幾乎可以肯定直接向它傳入二維數組毫無意義。

7.18    當數組是函數的參數時,為什麼sizeof 不能正确報告數組的大小?

    編譯器把數組參數當作指針對待,因而報告的是指針的大小。

8.10    在調用malloc() 的時候,錯誤"不能把void * 轉換為int *" 是什麼意思?

    說明你用的是C++ 編譯器而不是C 編譯器。

8.19    當我malloc() 為一個函數的局部指針配置設定記憶體時,我還需要用free() 明确的釋放嗎?

    是的。記住指針和它所指向的東西是完全不同的。局部變量在函數傳回時就會釋放,但是在指針變量這個問題上,這表示指針被釋放,而不是它所指向的對 象。用malloc() 配置設定的記憶體直到你明确釋放它之前都會保留在那裡。一般地,對于每一個malloc() 都必須有個對應的free() 調用。

8.22    我有個程式配置設定了大量的記憶體,然後又釋放了。但是從作業系統看,記憶體的占用率卻并沒有回去。

    多數malloc/free 的實作并不把釋放的記憶體傳回作業系統,而是留着供同一程式的後續malloc() 使用。

9.5    我認為我的編譯器有問題: 我注意到sizeof(’a’) 是2 而不是1 (即,不是sizeof(char))。

    可能有些令人吃驚,C語言中的字元常量是int 型,是以sizeof(’a’) 是sizeof(int),這是另一個與C++ 不同的地方。

11.5    一個頭檔案可以包含另一頭檔案嗎?

    這是個風格問題,是以有不少的争論。

    很多人認為"嵌套包含檔案" 應該避免:它讓相關定義更難找到; 如果一個檔案被包含了兩次,它會導緻重複定義錯誤;它會增大編譯時間,同時會令makefile 的人工維護十分困難。

    另一方面,它使子產品化使用頭檔案成為一種可能(一個頭檔案可以包含它所需要的一切,而不是讓每個源檔案都包含需要的頭檔案); 類似grep 的工具(或tags 檔案) 使搜尋定義十分容易,無論它在哪裡;

    一種流行的頭檔案定義技巧是:

    #ifndef HFILENAME_USED

    #define HFILENAME_USED

    ......

    #endif

    每一個頭檔案都使用了一個獨一無二的宏名。這令頭檔案可自我識别,以便可以安全的多次包含; 而自動Makefile 維護工具(無論如何,在大型項目中都是必不可少的) 可以很容易的處理嵌套包含檔案的依賴問題。

11.12    sizeof 操作符可以用于#if 預編譯指令中嗎?

    不行。預編譯在編譯過程的早期進行,此時尚未對類型名稱進行分析。作為替代,可以考慮使用ANSI 的<limits.h> 中定義的常量,或者使用"配置"(configure) 腳本。更好的辦法是,書寫與類型大小無關的代碼。

12.7    我不明白為什麼我不能象這樣在初始化和數組次元中使用常量:const int n = 5; int a[n];

     const 限定詞真正的含義是"隻讀的"; 用它限定的對象是運作時不能被指派的對象。是以用const 限定的對象的值并不完全是一個真正的常量。在這點上C 和C++ 不一樣。

12.10    為什麼我不能向接受const char ** 的函數傳入char **?

    你可以向接受const-T 的指針的地方傳入T 的指針(任何類型T 都适用)。但是,這個允許在帶修飾的指針類型上輕微不比對的規則卻不能遞歸應用,而隻能用于最上層。

    如果你必須指派或傳遞除了在最上層還有修飾符不比對的指針,你必須明确使用類型轉換(本例中,使用(const char **)),不過,通常需要使用這樣的轉換意味着還有轉換所不能修複的深層次問題。

12.11    怎樣正确聲明main()?

    int main(),int main(void) 或者int main(int argc,char *argv[]) (顯然argc 和argv 的拼寫可以随便)。

12.12    我能否把main() 定義為void,以避免擾人的"main無傳回值"警告?

    不能。main() 必須聲明為傳回int,沒有參數或者接受适當類型的兩個參數。如果你調用了exit() 但還是有警告資訊,你可能需要插入一條備援的return語句(或者使用某種"未到達" 指令,如果有的話)。把函數聲明為void 并不僅僅關掉了警告資訊:它可能導緻與調用者(對于main(),就是C 運作期初始代碼) 期待的不同的函數調用/傳回順序。

12.13    可main() 的第三個參數envp 是怎麼回事?

    這是一個盡管很常見但卻非标準的擴充。如果你真的需要用getenv() 函數提供的标準方法之外的辦法讀寫環境變量,可能使用全局變量environ 會更好——盡管它也同樣并不标準。

12.16    我一直用的那本書《熟練傻瓜C語言》總是使用void main()。

    可能這本書的作者把自己也歸為目标讀者的一員。很多書不負責任地在例子中使用void main(),并宣稱這樣是正确的。但他們錯了。

12.22    "#pragma once" 是什麼意思?我在一些頭檔案中看到了它。

    這是某些預處理器實作的擴充用于使頭檔案自我識别;它跟問題11.5 中講到的#ifndef 技巧等價,不過移植性差些。

12.25    memcpy() 和memmove() 有什麼差別?

    如果源和目的有重疊,memmove() 提供有保證的行為。而memcpy()則不能提供這樣的保證,是以可以實作得更加有效率。如果有疑問,最好使用memmove()。

12.27    為什麼ANSI 标準規定了外部标示符的長度和大小寫限制?

    問題在于連接配接器既不受ANSI/ISO 标準的控制也不遵守C 編譯器開發者的規定。

13.3    為什麼這些代碼while(!feof(infp)) { fgets(buf,MAXLINE,infp); fputs(buf,outfp); } 把最後一行複制了兩遍?

    在C 語言中,隻有函數讀并失敗以後才能得到檔案結束符。換言之,C 的I/O 和Pascal 的不一樣。通常你隻需要檢查輸入例程的傳回值,例如,fgets()在遇到檔案結束符的時候傳回NULL。實際上,在任何情況下都完全沒有必要使用feof()。

13.6    我如何在printf 的格式串中輸出一個’%’?我試過/%,但是不行。

    隻需要重複百分号: %%。n%不行,因為反斜杠'/'是C編譯器的轉義字元,而這裡我們的問題是printf中的轉義字元。

13.9    我如何用printf 實作可變的域寬度?就是說,我想在運作時确定寬度而不是使用%8d?

    printf("%*d",width,x) 就能達到你的要求。

13.16    我用scanf %d 讀取一個數字,然後再用gets() 讀取字元串,但是編譯器好像跳過了gets() 調用!

    scanf %d 不處理結尾的換行符。如果輸入的數字後邊緊接着一個換行符,則換行符會被gets() 處理。

    作為一個一般規則,你不能混用scanf() 和gets();scanf 對換行符的特殊處理幾乎一定會帶來問題。要麼就用scanf() 處理所有的輸入,要麼幹脆不用。

13.17    我發現如果堅持檢查傳回值以確定使用者輸入的是我期待的數值,則scanf() 的使用會安全很多,但有的時候好像會陷入無限循環。

    在scanf() 轉換數字的時候,它遇到的任何非數字字元都會終止轉換并被保留在輸入流中。是以,除非采用了其它的步驟,那麼未預料到的非數字輸入會不斷阻塞scanf(): scanf() 永遠都不能越過錯誤的非數字字元而處理後邊的合法數字字元。如果使用者在數字格式的scanf 如%d 或%f 中輸入字元‘x’,那麼提示後并用同樣的scanf() 調用重試的代碼會立即遇到同一個’x’。

13.18    為什麼大家都說不要使用scanf()?那我該用什麼來代替呢?

    scanf() 有很多問題而且它的%s 格式有着和gets() 一樣的問題——很難保證緩沖區不出現溢出情況。

    更一般地講,scanf() 的設計适用于相對結構化的、格式整齊的輸入。設計上,它的名稱就是來自于"scan formatted"。如果你留心的話,它會告訴你成功或失敗,但它隻能提供失敗的大略位置,至于失敗的原因,就無從得知了。

13.20    為什麼大家都說不要使用gets()?

    跟fgets() 不同,gets() 不能指明輸入緩沖區的大小,是以不能避免緩沖區的溢出。标準庫的fgets() 函數對gets() 作了很大的改進,盡管它仍然不完善。

13.22    fgetops/fsetops 和ftell/fseek 之間有什麼差別? fgetops() 和fsetops() 到底有什麼用處?

    ftell() 和fseek() 用長整型表示檔案内的偏移(位置),是以,偏移量被限制在2**31-1以内。而新的fgetpos() 和fsetpos() 函數使用了一個特殊的typedef fpos_t 來表示偏移量。這個typedef會選擇合适的值,是以,fgetpos() 和fsetpos 可以表示任意大小的檔案偏移。fgetpos() 和gsetpos() 也可以用來記錄多位元組流式檔案的狀态。

13.23    如何清除多餘的輸入,以防止在下一個提示符下讀入?fflush(stdin)可以嗎?

    fflush() 僅對輸出流有效。它對"flush" 的定義是用于完成輸出緩沖字元的輸出,而不是放棄剩餘的輸出緩沖。

14.6    我想用strcmp() 作為比較函數,調用qsort() 對一個字元串數組排序,但是不行。

    你說的"字元串數組" 實際上是"字元指針數組"。qsort 比較函數的參數是被排序對象的指針,在這裡,也就是字元指針的指針。然而strcmp() 隻接受字元指針。是以,不能直接使用strcmp()。寫一個下邊這樣的間接比較函數:

    int pstrcmp(const void *p1,const void *p2)

    {

    return strcmp(*(char * const *)p1,*(char * const *)p2);

    }

    比較函數的參數表示為"一般指針" const void *。然後,它們被轉換回本來表示的類型(指向字元指針的指針),再解引用,生成可以傳入strcmp() 的char*。

    不要被[K&R2] 5.11 節119-20頁的讨論所誤導,那裡讨論的不是标準庫中的qsort。

14.14    怎樣獲得在一定範圍内的随機數?

    直接的方法是      rand() % N     

    但這個方法不好,因為許多随機數發生器的低位比特并不随機。

    一個較好的方法是:    (int)((double)rand() / ((double)RAND_MAX + 1) * N)

    如果你不希望使用浮點,另一個方法是:rand() / (RAND_MAX / N + 1)

    RAND_MAX 是個常數,它告訴你C 庫函數rand() 的固定範圍。

14.18    我不斷得到庫函數未定義錯誤,但是我已經#inlude 了所有用到的頭檔案了。

    通常頭檔案隻包含外部說明。某些情況下,特别是如果是非标準函數,當你連接配接程式時需要指定正确的函數庫以得到函數的義。

15.4    浮點計算程式表現奇怪,在不同的機器上給出不同的結果。

    回想一下,電腦一般都是用一種浮點的格式來近似的模拟實數的運算,注意是近似而不是精确。下溢、誤差的累積和其它非正常性是常遇到的麻煩。

    不要假設浮點運算結果是精确的,特别是不要假設兩個浮點值可以進行等值比較。

15.7    為什麼C 不提供幂 的運算符?

   因為提供幂乘指令的處理器非常少。

16.3    為什麼當n 為long int,printf("%d",n); 編譯時沒有比對警告?我以為ANSI 函數原型可以防止這樣的類型不比對。

      當一個函數用可變參數時,它的原型說明沒有也不能提供可變參數的數目和類型。是以通常的參數比對保護不适用于可變參數中的可變部分。編譯器不能執行内含的轉換或警告不比對問題。

16.8    為什麼編譯器不讓我定義一個沒有固定參數項的可變參數函數?

     标準C 要求用可變參數的函數至少有一個固定參數項,這樣你才可以使用va start()。是以編譯器不會接受下面定義的函數:

     int f(...)

     {

     ...

     }

16.10    va_arg() 不能得到類型為函數指針的參數。

    宏va_arg() 所用的類型cast不能很好地操作于象函數指針這類過度複雜的類型。但是如果你用typedef 定義一個函數指針類型,那就一切正常了。

17.4     程式執行正确,但退出時崩潰在main() 最後一個語句之後。為什麼會這樣?

    注意是否錯誤說明了main()。是否把局部緩沖傳給了setbuf() 或setvbuf()。又或者問題出在注冊于atexit() 的清理函數。

21.12    指針真得比數組快嗎?

    一般的機器, 通常周遊大的數組時用指針比用數組要快, 但是某些處理器就相反。

21.28    (year%4 == 0) 是否足夠判斷潤年?2000 年是閏年嗎?

    這個測試并不足夠 (2000 年是閏年)。對于目前用的格裡高力曆法, 完整的表達式為:

    year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)

繼續閱讀