《你必须知道的495个C语言问题》知识笔记及补充
1. extern在函數(shù)聲明中是什么意思?
它可以用作一種格式上的提示表明函數(shù)的定義可能在另一個(gè)源文件中,但在
extern int f(); 和 int f(); 之間并沒(méi)有實(shí)質(zhì)的區(qū)別。
補(bǔ)充:extern可以置于變量或者函數(shù)前,以標(biāo)示變量或者函數(shù)的定義在別的文件中,提示編譯器遇到此變量和函數(shù)時(shí)在其他模塊中尋找其定義。此外extern也可用來(lái)進(jìn)行鏈接指定。也就是說(shuō)extern有兩個(gè)作用:
第 一,當(dāng)它與"C"一起連用時(shí),如: extern "C" void fun(int a, int b);則告訴編譯器在編譯fun這個(gè)函數(shù)名時(shí)按著C的規(guī)則去翻譯相應(yīng)的函數(shù)名而不是C++的,C++的規(guī)則在翻譯這個(gè)函數(shù)名時(shí)會(huì)把fun這個(gè)名字變得面目 全非,可能是fun@aBc_int_int#%$也可能是別的,這要看編譯器的"脾氣"了(不同的編譯器采用的方法不一樣),為什么這么做呢,因?yàn)?C++支持函數(shù)的重載;
第二,當(dāng)extern不與"C"在一起修飾變量 或函數(shù)時(shí),如在頭文件中: extern int g_Int;?它的作用就是聲明函數(shù)或全局變量的作用范圍的關(guān)鍵字,其聲明的函數(shù)和變量可以在本模塊活其他模塊中使用,記住它是一個(gè)聲明不是定義!也就是 說(shuō)B模塊(編譯單元)要是引用模塊(編譯單元)A中定義的全局變量或函數(shù)時(shí),它只要包含A模塊的頭文件即可,在編譯階段,模塊B雖然找不到該函數(shù)或變量, 但它不會(huì)報(bào)錯(cuò),它會(huì)在連接時(shí)從模塊A生成的目標(biāo)代碼中找到此函數(shù)。
2. 怎樣建立和理解非常復(fù)雜的聲明?例如定義一個(gè)包含N個(gè)指向返回指向字符的指針的函數(shù)的指針的數(shù)組?
這個(gè)問(wèn)題至少有以下3種答案:
(1)char *(*(*a[N])())();
(2)用typedef逐步完成聲明:
typedef char *pc; ? ? ? ?/* 字符指針 */
typedef pc fpc(); ? ? ? ?/* 返回字符指針的函數(shù) */
typedef fpc *pfpc; ? ? ? /* 上面函數(shù)的指針 */
typedef pfpc fpfpc(); ? ?/* 返回函數(shù)指針的函數(shù) */
typedef fpfpc *pfpfpc; ? /* 上面函數(shù)的指針 */
pfpfpc a[N]; ? ? ? ? ? ? /* 上面指針的數(shù)組 */
(3)使用cdecl程序,它可以把英文翻譯成C或者把C翻譯成英文:
通過(guò)類型轉(zhuǎn)換,cdecl也可以用于解釋復(fù)雜的聲明,指出參數(shù)應(yīng)該進(jìn)入哪一對(duì)括號(hào)(如同在上述的復(fù)雜函數(shù)定義中)。
補(bǔ)充:cdecl程序非常有用,它可以在C語(yǔ)言的聲明和英語(yǔ)之間進(jìn)行轉(zhuǎn)換。它可以解釋一個(gè)現(xiàn)存的C語(yǔ)言聲明,cdecl 程序可以幫助你分析復(fù)雜的聲明。
一個(gè)在線的cdecl.
3. 為什么struct x { ... }; x thestruct; 不對(duì)?
C不是C++。結(jié)構(gòu)標(biāo)簽不能自動(dòng)生成類型。
補(bǔ)充:也就是在C++中這樣是對(duì)的。在C中不想用struct只能借助于typedef。
4. NULL可以確保是0,但空指針(null)卻不一定。
5. 數(shù)組和指針的區(qū)別是什么?
數(shù)組自動(dòng)分配空間,但是不能重分配或改變大小。指針必須明確賦值以指向分配的空間(可能使用malloc),但是可以隨意重新賦值(即指向不同的對(duì)象),同時(shí)除了表示一個(gè)內(nèi)存塊的基址之外,還有許多其它的用途。
由于數(shù)組和指針?biāo)^的等價(jià)性,數(shù)組和指針經(jīng)常看起來(lái)可以互換,而事實(shí)上指向malloc分配的內(nèi)存塊的指針通常被看作一個(gè)真正的數(shù)組(也可以用[]引用)。但是,要小心sizeof。
6. 當(dāng)我向一個(gè)接受指針的指針的函數(shù)傳入二維數(shù)組的時(shí)候,編譯器報(bào)錯(cuò)了。
數(shù)組蛻化為指針的規(guī)則不能遞歸應(yīng)用。數(shù)組的數(shù)組(即二維數(shù)組)蛻化為數(shù)組的指針,而不是指針的指針。數(shù)組指針常常令人困惑,需要小心對(duì)待:
如果你向函數(shù)傳遞二維數(shù)組:
int array[NROWS][NCOLUMNS];
f(array);
那么函數(shù)的聲明必須匹配:
void f(int a[][NCOLUMNS]){ ... }
或者
void f(int (*ap)[NCOLUMNS]){ ... } ?/* ap是個(gè)數(shù)組指針 */
在第一個(gè)聲明中,編譯器進(jìn)行了通常的從“數(shù)組的數(shù)組”到“數(shù)組的指針”的隱式轉(zhuǎn)換;第二種形式中的指針定義顯而易見(jiàn)。
因?yàn)楸徽{(diào)函數(shù)并不為數(shù)組分配地址,所以它并不需要知道總的大小,所以行數(shù)NROWS可以省略。但數(shù)組的寬度依然重要,所以列維度NCOLUMNS(對(duì)于三維或多維數(shù)組,相關(guān)的維度)必須保留。
如果一個(gè)函數(shù)已經(jīng)定義為接受指針的指針,那么幾乎可以肯定直接向它傳入二維數(shù)組毫無(wú)意義。
7. 為什么這段代碼不行?
char *str;
gets(str);
printf("%s\n", str);
str沒(méi)有指向任何合法的位置,換言之,我們不知道指針str指向何處。因?yàn)榫植孔兞繘](méi)有初始化,通常包含垃圾信息,所以甚至都不能保證str是一個(gè)合法的指針。
改正方法:用局部變量或用malloc()分配str緩沖區(qū)。
8. 我剛才試了這樣的代碼char *p; strcpy(p, "abc"); 而它運(yùn)行正常?怎么回事?為什么它沒(méi)有崩潰?
未初始化的指針p所指向的隨機(jī)地址恰好對(duì)你來(lái)說(shuō)是可寫的,而且顯然也沒(méi)有用于什么關(guān)鍵的數(shù)據(jù)。
char *p; 編譯器只分配了足夠容納指針本身的內(nèi)存; 也就是說(shuō),這種情況下,你分配了sizeo(char*)個(gè)字節(jié)的內(nèi)存。但你還沒(méi)有分配任何讓指針指向的內(nèi)存,因此此時(shí)p所指向的內(nèi)存可能是垃圾信息也可能是可寫的區(qū)域,沒(méi)有崩潰說(shuō)明是后者。
9. 下面關(guān)于itoa的實(shí)現(xiàn)為什么是錯(cuò)誤的?如何改正?
char *itoa(int n)
{
? ? ?char retbuf[20]; ? // error
? ? ?sprintf(retbuf, "%d", n);
? ? ?return retbuf; ? ? // error
}
這樣在編譯的時(shí)候會(huì)出現(xiàn)這樣的警告信息“warning C4172: returning address of local variable or temporary”,說(shuō)明retbuf的地址是臨時(shí)變量,是暫時(shí)的,函數(shù)返回時(shí)就沒(méi)有了也就是不能直接返回。
一種解決方案是把返回緩沖區(qū)聲明為靜態(tài)變量:
static char retbuf[20];
總結(jié):若要返回字符串或其它集合,則返回指針必須是靜態(tài)分配的緩沖區(qū),或者調(diào)用者傳入的緩沖區(qū),或者用malloc()獲得的內(nèi)存,但不能是局部(自動(dòng))數(shù)組。
10. 為什么在調(diào)用free()之后指針沒(méi)有變空?使用(賦值,比較)釋放之后的指針有多么不安全?
當(dāng)你調(diào)用free()的時(shí)候,傳入指針指向的內(nèi)存被釋放,但調(diào)用函數(shù)的指針值可能保持不變,因?yàn)镃的按值傳參語(yǔ)義意味著被調(diào)函數(shù)永遠(yuǎn)不會(huì)改變參數(shù)的值。嚴(yán)格的說(shuō),被釋放的指針值是無(wú)效的,對(duì)它的任何使用,即使沒(méi)有解參照,也可能帶來(lái)問(wèn)題,盡管作為一種實(shí)現(xiàn)質(zhì)量的表現(xiàn),多數(shù)實(shí)現(xiàn)都不會(huì)對(duì)無(wú)傷大雅的無(wú)效指針使用產(chǎn)生例外。
補(bǔ)充:free()過(guò)后的指針也叫做“野指針”,建議free()過(guò)后立即將指針置為NULL,詳細(xì)原因請(qǐng)看“為什么free(re)過(guò)后re不為NULL呢?”。
11. calloc()和malloc()有什么區(qū)別?利用calloc的零填充功能安全嗎?free()可以釋放calloc()分配的內(nèi)存嗎,還是需要一個(gè)cfree()?
calloc(m, n)本質(zhì)上等價(jià)于
p = malloc(m * n);
memset(p, 0, m * n);
填充的全是零,因此不能確保生成有用的空指針值或浮點(diǎn)零值,free()可以安全的用來(lái)釋放calloc()分配的內(nèi)存。
12. 在C中,sizeof('a')=4而不是1,因?yàn)镃語(yǔ)言中的字符常數(shù)是int型,因此sizeof('a')是sizeof(int),這是與C++不同的地方。
13. C語(yǔ)言中沒(méi)有提高標(biāo)準(zhǔn)的布爾類型,需要用#define或枚舉常數(shù)定義true/false。
14. 我試圖用ANSI“字符串化”預(yù)處理操作符#向信息中插入符號(hào)常量的值,但它字符串化的總是宏的名字而不是它的值。
你可以用下面這樣的兩步方法迫使宏既字符串化又?jǐn)U展:
#define Str(x) #x
#define Xstr(x) Str(x)
#define OP plus
char *opname = Xstr(OP);
這段代碼把opname置為“plus”而不是“OP”。
總結(jié):在使用符號(hào)粘接操作符##連接兩個(gè)宏的值(而不是名字)時(shí)也要采用同樣的“迂回戰(zhàn)術(shù)”。
15. memcpy()和memmove()有什么區(qū)別?
如果源和目的參數(shù)有重疊,memmove()提供有保證的行為。
而memcpy()則不能提供這樣的保證,因此可以實(shí)現(xiàn)的更加有效率。
如果有疑問(wèn),最好使用memmvoe()。
16. 我如何在printf的格式串中輸出一個(gè)'%'?我試過(guò)"\%",但是不行。
只需要重復(fù)百分號(hào):%%。\%不行,因?yàn)閈是編譯器的轉(zhuǎn)義字符,而這里我們的問(wèn)題最終是printf的轉(zhuǎn)義字符。
17. 為什么大家都說(shuō)不要使用gets()?
跟fgets()不同,gets()不能被告知輸入緩沖區(qū)的大小,因此不能避免緩沖區(qū)的溢出。標(biāo)準(zhǔn)庫(kù)的fgets()函數(shù)對(duì)gets()作了很大的改進(jìn),盡管它仍不完善。如果真的可能輸入很長(zhǎng)的行,還是需要仔細(xì)思考,正確處理。
18. 一個(gè)粗陋但通常有效的測(cè)試NaN的方法:
#define isnan(x) ? ?((x) != (x))
C99提高isnan(), fpclassify()及其它一些類別的函數(shù)。
標(biāo)準(zhǔn)C函數(shù)庫(kù)的源代碼:
GNU工程有一個(gè)完全實(shí)現(xiàn)的C函數(shù)庫(kù)(http://www.gnu.org/software/libc/)
19. 怎樣判斷機(jī)器的字節(jié)順序是高字節(jié)在前還是低字節(jié)在前?
有個(gè)使用指針的方法:
int x = 1;
if(*(char *)&x == 1)
? ? printf("little-endian\n");
else
? ? printf("big-endian\n");
另外一個(gè)可能是用聯(lián)合。
20. 由一個(gè)日期,怎樣知道是星期幾?
用mktime()或localtime()(注:如果tm_hour的值為0,要注意DST(夏時(shí)制)的調(diào)整);或者Zeller的congruence;或者這個(gè)由Tomohiko Sakamoto提供的優(yōu)雅的代碼:
int dayofweek(int y, int m, int d) ?/* 0 = Sunday */
{
?? ?static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
?? ?y -= m < 3;
?? ?return (y + y/4 - y/100 + y/400 + t[m - 1] + d) % 7;
}
————————————————
版權(quán)聲明:本文為CSDN博主「小敏紙」的原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請(qǐng)附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/lanxuezaipiao/article/details/18953917
總結(jié)
以上是生活随笔為你收集整理的《你必须知道的495个C语言问题》知识笔记及补充的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Google Java编程风格指南中文版
- 下一篇: Tomcat 部署项目的三种方法