博客鏈接: http://codeshold.me/2017/02/expert_c_PRogramming.html
讀一本書必輸出一篇筆記或者總結(jié)!!! 《C專家編程》這本書很早看完了,但整理筆記卻斷斷續(xù)續(xù)的花了三天時間,這從側(cè)面更說明了這本書的經(jīng)典了(盡管不到300頁)! 至此C經(jīng)典著作《C Traps and Pitfalls》《Expert C Programming》《POINTER ON C》已經(jīng)算完整的看完了……
typedef struct bar {int bar;} bar; 中bar的含義為什么extern char *p和另一個文件中的char p[100]不能匹配?什是總線錯誤(bus error)?什么是段違規(guī)/段錯誤(segmentation violation)?char *foo[]和char (*foo)[]有何不同?計(jì)算機(jī)日期
UNIX的系統(tǒng)時間是從1970年1月1日(UTC)起按秒算的timestamp是從1970年開始的先有UNIX,再有C關(guān)于time_t什么時候會重新回到開始?即達(dá)到盡頭,運(yùn)行如下代碼可查詢(2038年)
C語言排斥強(qiáng)類型,即其是弱類型
C語言的許多特性是為了方便編譯器設(shè)計(jì)者而建立的:
數(shù)組下表從0開始基本數(shù)據(jù)類型直接和底層硬件相關(guān)auto 關(guān)鍵字是擺設(shè)(它是缺省的內(nèi)存分配模式,其只對創(chuàng)建符號表入口的編譯器設(shè)計(jì)者有用)float被自動擴(kuò)展為double(但在ANSI C中不在這樣)不允許嵌套,即函數(shù)內(nèi)部包含另外一個函數(shù)C和shell
Steve Bourne編寫的UNIX shell時,創(chuàng)立了一個C語言的變型, 其使用了很多“顯示的結(jié)束語句”,如if...fi,且shell不用malloc,而使用sbrk自行負(fù)責(zé)堆存儲管理,提高字符串處理效率。Broune 事實(shí)上促成了國際C語言混亂代碼大賽(The International Obfuscated C Code Competition)應(yīng)使用ANSI C 而不是 K&R C
ANSI C 對編譯器的部分要求如下
在函數(shù)定義中形參個數(shù)的上限至少可以達(dá)到31個在函數(shù)調(diào)用中實(shí)參個數(shù)的上限至少可以達(dá)到31個ANSI C 與 K&R C 的不同
新的、非常不同,且重要的(僅一個) ANSI C 把函數(shù)原型作為函數(shù)聲明的一部分,原型的形式,其兩者也有了很大的變化新增的關(guān)鍵字 ANSI C 增加了 enum, const, volatile, signed, void等關(guān)鍵字棄掉了K&R C中的entry等關(guān)鍵字“安靜的改變”
相鄰字符串的面值會被自動連接在一起尋常算數(shù)轉(zhuǎn)換(usual arithmetic conversation)K&R C 采用的是無符號保留(unsigned preserving)原則,即當(dāng)一個無符號類型與int或更小的整型混合使用時,結(jié)果類型是無符號類型。ANSI C 采用的是值保留(value preserving)原則,即當(dāng)執(zhí)行算數(shù)運(yùn)算時,如果類型不同,就會發(fā)生轉(zhuǎn)換。數(shù)據(jù)類型朝著浮點(diǎn)精度更高、長度更長的方向轉(zhuǎn)換,整形數(shù)如果轉(zhuǎn)換為signed不會丟失信息,就轉(zhuǎn)換為signed,否則轉(zhuǎn)換為unsigned —— 即包括整型升級和尋常算數(shù)轉(zhuǎn)換
main(){ if(-1 < (unsigned char)1) printf("-1 is less than (unsigned char)1: ANSI semantics"); else printf("-1 NOT less than (unsigned char)1: K&R C semantics");}除上面之外的其他區(qū)別符號粘貼(token-pasting)三字母詞(trigraph),即用3個字符表示一個單獨(dú)的字符,如兩字母詞/t表示“tab”, 三字母詞??<表示“開放的花括號”實(shí)參、形參的匹配
如下代碼會報(bào)一條warning,“argument #1 is imcompatible with prototype…”,為什么?
foo(const char **p) {}main(int argc, char **argv) { foo(argv);}原因分析(摘自ANSI C標(biāo)準(zhǔn))
每個實(shí)參都應(yīng)該具有自己的類型,這樣它的值就可以復(fù)制給與它所對應(yīng)的形參類型的對象(該對象的類型不能含有限定符)要使得上述賦值形式合法,必須滿足下列條件之一: 兩個操作數(shù)都是指向有限定符或無限定符的相容類型指針左邊指針?biāo)赶虻念愋捅仨毦哂杏疫呏羔標(biāo)赶蝾愋偷娜肯薅ǚ谏鲜雒枋?code>故實(shí)參char *能和型參const char*匹配const float *類型并不是一個有限定符的指針類型,它的類型是“指向一個具有const限定符的float類型的指針,即const修飾的是指針指向的類型而不是指針本身基于上條描述故char ** 和 const char ** 都是沒有限定符的指針類型,但它們所指向的類型不一樣,進(jìn)而不相容, 所以報(bào)錯const和*通常只用于數(shù)組形式的參數(shù)中模擬傳值調(diào)用!
#pragma用于向編譯器提示一些信息,諸如希望把某個特定函數(shù)擴(kuò)展為內(nèi)斂函數(shù),或者取消邊界的檢查。
一個‘L’的 NUL 用于結(jié)束一個ACSII 字符串;兩個‘LL’的 NULL 用于表示什么也不指向(空指針)
一個遵循標(biāo)準(zhǔn)的C編譯器至少允許一條switch語句中有257個case標(biāo)簽
使用switch... case...break...時,養(yǎng)成添加/* fall through */的習(xí)慣
字符串?dāng)?shù)組初始化(枚舉聲明、單行多變量聲明等),最后一個尾巴,,ANSI C rationel對其的解釋是:它使得C語言在自動生成時更容易些!
幾乎沒有人習(xí)慣在函數(shù)名前添加存儲類型說明符,所以絕大多數(shù)函數(shù)都是全局可見
C語言的符號重載
static 在函數(shù)內(nèi)部時,表示該變量的值在各個調(diào)用間一直保持著連續(xù)性;……void 位于參數(shù)列表中,表示沒有參數(shù);……() 調(diào)用一個函數(shù);定義帶參數(shù)的宏;包圍sizeof操作符的操作數(shù)(如果它是類型名)當(dāng)sizeof操作數(shù)是個類型時,其必須加上(),若是變量則不必加括號(建議加)i = 1, 2; 中 i 的結(jié)果是?(1)
/可對一些字符轉(zhuǎn)義,包括newline(即回車鍵,表示連接)
不充分的參數(shù)解析,shell參數(shù)解析
找出目錄中的鏈接文件ls -l | grep ->,ls -l | grep "->" 均不行ls -AF | grep "@" 或者 file -h | grep linkratio = *x/*y; 會報(bào)錯??
錯誤檢查程序,lint程序
存儲類型說明符(storage-class):extern static register auto typedef
類型限定符(type-qualifier): const volatile
“在函數(shù)調(diào)用時,參數(shù)按照從右到左的次序壓到堆棧里”這種說法過于簡單,參數(shù)在傳遞時首先盡可能地存放到寄存器中(追求速度)
結(jié)構(gòu)體
一般形式
struct 結(jié)構(gòu)標(biāo)簽 (可選) { 類型1 標(biāo)志符1; 類型2 標(biāo)志符2; ……} 變量定義(可選);結(jié)構(gòu)中允許存在位段、無名字段以及字對齊所需的填充字段
struct pid_tag { unsigned int inactive : 1; unsigned int : 1; // 1個位的填充 unsigned int refcount : 6; unsigned int : 0; //填充到下一個字邊界 short pid_id; struct pid_tag *link;}位段的類型必須是int,unsigned int 或 signed int(或加上限定詞)
聯(lián)合
一般形式
union 結(jié)構(gòu)標(biāo)簽 (可選) { 類型1 標(biāo)志符1; 類型2 標(biāo)志符2; ……} 變量定義(可選);節(jié)省存儲空間 && 提取單獨(dú)的字節(jié)字段(聯(lián)合不需要額外的賦值和強(qiáng)制類型轉(zhuǎn)換,同一個數(shù)據(jù)可解釋為兩個不一樣的東西)
如下value.byte.c0
枚舉
把一串名字和一串整型值聯(lián)系在一起如果const(或)volatile關(guān)鍵字的后面緊跟著類型說明符(如int,long等),它作用于類型說明符。在其他情況下,const和(或)volatil 關(guān)鍵字作用于它左邊緊鄰的指針星號
分析以下聲明:
char * const *(*next)();char *(* c[10])(int **p);typedef關(guān)鍵字并不是創(chuàng)建一個變量,而是宣稱“這個名字是指定類型的同義詞”
typedef struct foo{...foo;}的含義
typedef struct baz {int baz;} baz; 即相當(dāng)于 typedef struct baz {int baz;} baz_type;typedef聲明引入了baz_type作為struct baz {int baz;}的簡寫形式struct baz xxxxx;使用的是結(jié)構(gòu)標(biāo)簽baz yyyyy; 使用的是結(jié)構(gòu)類型編寫C語言聲明解釋程序cdecl
數(shù)組和指針并不相同
對編譯器而言,一個數(shù)組就是一個地址,一個指針就是一個地址的地址。char *p = "abcdefgh"; ...p[3] 先取符號表中p的地址;提取存儲于此處的指針;把偏移量和指針相加,產(chǎn)生一個地址;訪問這個地址,取得內(nèi)容char a[] = "abcdefgh"; ....a[3] 先取符號表中a的地址;把偏移量和這個地址相加,產(chǎn)生一個地址;訪問這個地址,取得內(nèi)容初始化指針時所創(chuàng)建的字符串常量被定義為只讀!extern int *x;和extern int x[] 區(qū)別?
聲明和定義
聲明相當(dāng)于普通的聲明:它所說明的并非本身,而是描述其他地方的創(chuàng)建的對象定義相當(dāng)于特殊的聲明:它為對象分配內(nèi)存定義是聲明的特殊情況,它分配內(nèi)存空間,并可能提供一個初始值左值:可修改的左值(允許出現(xiàn)在復(fù)制語句左邊)和不可修改的左值
數(shù)組名是左值,但不能作為賦值的對象(左值即為可取地址的值),編譯器為每隔對象分配一個地址(左值) 回文!表達(dá)式中數(shù)組名(與聲明不同)被編譯器當(dāng)作一個指向該數(shù)組第一個元素的指針
“表達(dá)式中的數(shù)組”就是指針a[6] = ...; 6[a] = ...; 兩種形式都正確
下標(biāo)總是與指針的偏移量相同
C語言把數(shù)組下標(biāo)作為指針的偏移量處理以一維數(shù)組時,指針并不見的比數(shù)組快在函數(shù)參數(shù)的聲明中,數(shù)組名被編譯器當(dāng)作指向該數(shù)組第一個元素的指針
“作為函數(shù)參數(shù)的數(shù)組名”等同于指針下面代碼運(yùn)行正常
fun2(int arr[]) { arr[1] = 3; *arr = 3; arr = array2;}在C語言中,所有非數(shù)組形式的數(shù)據(jù)均以傳值形式調(diào)用
指針就是指針,只是可以通過下表的形式對其進(jìn)行訪問
用a[i]這樣的形式對數(shù)組進(jìn)行訪問總是被編譯器“改寫”或解釋為像*(a+1)這樣的指針訪問
多維數(shù)組初始化時,可省略最左邊下標(biāo)的長度(也只能是最左邊),如int rhubarb[][3] = { {0, 0, 0}, {1, 1, 1},};
sizeof(數(shù)組名)返回的是數(shù)組總的字節(jié)數(shù)
下面代碼,運(yùn)算結(jié)果如下 sizeof(str):15 func sizeof(str):8
指針數(shù)組就是Iliffe向量, char *pea[4]
“數(shù)組名被改寫成一個指針參數(shù)”規(guī)則并不是遞歸定義的。數(shù)組的數(shù)組會被改寫成為“數(shù)組的指針”,而不是“指針的指針”
對應(yīng)列表
| 實(shí)參 | 所匹配的形式參數(shù) |
|---|---|
數(shù)組的數(shù)組char c[8][10]; | char (*)[10] 數(shù)組指針 |
指針數(shù)組char *c[15] | char **c 指針的指針 |
數(shù)組指針(行指針)char (*c)[64] | char (*c)[64] 不改變 |
指針的指針 char **c | char **c 不改變 |
代碼
func1(int fruit[2][3][4]) { ; }func2(int fruit[][3][4]) { ; }func3(int (*fruit)[3][4]) { ; }向函數(shù)傳遞一個一位數(shù)組:增加一個額外的參數(shù)或者賦予數(shù)組最后一個元素一個特殊的值
向函數(shù)傳遞一個普通的多維數(shù)組:必須提供除了最左邊一維以外多有維的長度。即多維數(shù)組最主要的一維長度不必顯式書寫。
strings實(shí)用程序可幫助從二進(jìn)制文件內(nèi)部查看程序可能產(chǎn)生的錯誤。
cc -S -Xc banana.c, -S選項(xiàng)使編譯器停在匯編階段,-Xc選項(xiàng)告訴編譯器拒絕任何不符合ANSI C的代碼結(jié)構(gòu)鏈接
鏈接器(linker)
編譯器中單獨(dú)分離出來的程序包括:預(yù)處理器(preprocessor)、語法和語義檢查器(syntactic and semantic checker)、代碼生成器(code generator)、匯編程序(assembler)、優(yōu)化器(optimizer)、鏈接器(linker)等。-#選項(xiàng)查看編譯過程的各個獨(dú)立階段通過給編譯器驅(qū)動器一個特殊的-W選項(xiàng)(表示傳遞這個選項(xiàng)到那個階段)向各個階段傳遞選項(xiàng)信息,如cc -W1, -m mainc > main.linker.map,其中-m選項(xiàng)是傳遞給鏈接-載入器的,要求其產(chǎn)生連接器映像動態(tài)鏈接的主要目的就是把程序與它們使用的特定函數(shù)庫版本中分離出來。這種介于應(yīng)用程序和函數(shù)庫二進(jìn)制可執(zhí)行文件所提供的服務(wù)之間的接口,稱之為二進(jìn)制接口(application Binary Interface, ABI)
動態(tài)鏈接庫,可由ld創(chuàng)建,后綴名約定以.so結(jié)尾,表示shared object,簡單的可以通過cc的-G選項(xiàng)來創(chuàng)建動態(tài)鏈接必須保證4個特定的函數(shù)庫:libc(C運(yùn)行時庫), libsys(其他系統(tǒng)函數(shù)), libX(X Windowing), libnsl(網(wǎng)絡(luò)服務(wù))靜態(tài)庫稱作為archive,通過ar來創(chuàng)建和更新,后綴名約定以.a結(jié)尾
cc -o libfruit.so -G tomoto.c使用示例 cc test.c -L/home/swf -R/home/swf -lfruit, -L, -R 分別告訴鏈接器在鏈接和運(yùn)行時從哪個目錄找需要鏈接的函數(shù)庫-lthread選項(xiàng)告訴編譯鏈接到libthread.so,即libname.so對應(yīng)于-lname
編譯器希望在確定的目錄下找到庫,鏈接時一般使用-Lpathname,-Rpathname,默認(rèn)讀取系統(tǒng)變量LD_LIBRARY_PATH和LD_RUN_PATH等
文件名通常不與其所對應(yīng)的函數(shù)庫名相似
#include文件名 | 庫路徑名 | 所用的編譯器選項(xiàng) |
|---|---|---|
| math.h | /usr/lib/libm.so | -lm |
| math.h | /usr/lib/libm.a | -dn -lm |
| stdio.oh | /usr/lib/libc.so | 自動鏈接 |
| /usr/openwin/include/X11.h | /usr/openwin/lib/libX11.so | -L/usr/openwin/lib -lX11 |
| thread.h | /usr/lib/libthread.so | -lthread |
| curses.h | /usr/lib/curses.a | -lcurses |
| sys/socker.h | /usr/lib/libsocket.so | -lsocket |
nm工具可列出函數(shù)庫中包含的函數(shù), nm libc.so | grep xdr_reference
始終將-l函數(shù)庫選項(xiàng)放在編譯命令的最右邊,很多人習(xí)慣<命令><選項(xiàng)><文件>,但鏈接器采用這個容易引起混淆
Interpositioning就是通過編寫與庫函數(shù)同名的函數(shù)來取代該庫函數(shù)的行為。
準(zhǔn)則:不要讓程序中的任何符號成為全局的,除非有意將其作為程序的接口之一。很多頭文件中的函數(shù)有存儲類型符static
避免使用的標(biāo)識符(P104)
ANSI C 標(biāo)準(zhǔn)規(guī)定,對于外部標(biāo)識符,編譯器可以自行定義,使其不區(qū)分字母大小寫。同時,外部標(biāo)識符的前六個字符必須與其他標(biāo)識符不同。
a.out 是 assemble output (匯編程序輸出)的縮寫
UNIX中可執(zhí)行文件是以一種特殊的方式加上標(biāo)簽的,這樣系統(tǒng)就能夠確認(rèn)它的屬性。
為重要的數(shù)字定義標(biāo)簽,用獨(dú)特的數(shù)字唯一地標(biāo)識數(shù)據(jù),是一種普遍采用的編程技。標(biāo)簽所定義的數(shù)字通常被稱為“神奇”數(shù)字ELF (Executable and Linking Format)可執(zhí)行文件和鏈接格式。UNIX中可man a.out 查看有關(guān)UNIX系統(tǒng)所使用的格式的信息。
段 segments
unix中,段表示一個二進(jìn)制文件相關(guān)的內(nèi)容塊Intel x86的內(nèi)存模型中,段表示一個設(shè)計(jì)的結(jié)果,其中地址空間并非一個整體,而是分成了一些64K大小的區(qū)域,稱之為段。size a.out 可查看可執(zhí)行文件中的三個段(文本段、數(shù)據(jù)段、bss段)
查看可執(zhí)行文件的內(nèi)容,nm和dump工具也可以
BSS段這個名字是“Block Started by Symble” 由符號開始的塊的縮寫,其不保存在目標(biāo)文件中(除了記錄BSS段在運(yùn)行時所需要的大小)。
a.out
數(shù)據(jù)段保存在目標(biāo)文件中,存儲初始化的全局和靜態(tài)變量以及它們的值BSS段不保存在目標(biāo)文件中(除了記錄BSS段在運(yùn)行時所需要的大小)文本段是最容易受優(yōu)化措施影響的段,存儲可執(zhí)行文件的指令a.out文件的大小受調(diào)試狀態(tài)下編譯的影響,但段不受影響在操作系統(tǒng)中段就是一片連續(xù)的虛擬地址
函數(shù)調(diào)用:過程活動記錄 (可參考CSAPP)
頭文件/usr/include/sys/frame.h描述了過程活動記錄在unix系統(tǒng)中的樣子懸掛指針 dangling pointer
存儲類型關(guān)鍵字:
static 可保證數(shù)據(jù)存在數(shù)據(jù)段中而不是堆棧中auto 通常由編譯器設(shè)計(jì)者使用,用于標(biāo)記符號表的條目——它表示“在進(jìn)入該塊之后,自動分配存儲”(與編譯時靜態(tài)分配或在堆上動態(tài)分配不同)
register int filbert;auto int almond;static int hazel;控制
setjmp()和longjmp()是通過操作過程活動記錄來完成的,其在C++中變異為更普通的異常處理機(jī)制catch和throw (P128)goto語言不能跳出C語言當(dāng)前的函數(shù)longjmp()可以跳回到曾經(jīng)到過的地方setjmp()/longjmp()最大的用途是錯誤恢復(fù)
#inlcude <setjmp.h>jump_buf buf;banna(){ printf("in banna() /n"); longjmp(buf, 1); /*以下代碼不會被執(zhí)行*/ printf("you'll never see this, because i longjmp'd ");}main(){ if(setjmp(buf)) printf("back in main/n"); else { printf("first time through/n"); banana(); }}有用的C語言工具
檢查源碼的工具:indent(C程序美化器,和cb類似), cflow(打印程序中調(diào)用者/被調(diào)用著的關(guān)系) cscope(一個基于ASCII碼C程序的交互式瀏覽器), ctags(創(chuàng)建一個標(biāo)簽文件), lint(C程序檢查器), vgrind(格式器,用于打印漂亮的C列表)檢查可執(zhí)行文件的工具:dis(目標(biāo)代碼反匯編工具),dump -Lv(打印動態(tài)鏈接信息), ldd(打印文件所需的動態(tài)), nm(打印目標(biāo)文件的符號列表), strings(查看嵌入二進(jìn)制文件中的字符串), sum(打印文件的校驗(yàn)和與程序塊計(jì)數(shù))幫助調(diào)試工具:truss(打印可執(zhí)行文件所進(jìn)行的系統(tǒng)調(diào)用), ps, ctrace(修改你的源文件,文件執(zhí)行時按行打印), debugger(交互式調(diào)試器), file性能優(yōu)化工具:tcov(顯示每條語句執(zhí)行次數(shù)的計(jì)數(shù)), time(實(shí)際時間和CPU時間), prof(每隔程序所消耗時間的百分比), gprof(調(diào)用圖配置數(shù)據(jù))標(biāo)準(zhǔn)的代碼優(yōu)化技巧:消除循環(huán);函數(shù)代碼就地?cái)U(kuò)展;公共子表達(dá)式消除、改進(jìn)寄存器分配、省略運(yùn)行時對數(shù)組邊界的檢查、循環(huán)不變量代碼移動(loop-invariant code motion)、操作符長度削減(指針操作符轉(zhuǎn)變?yōu)槌朔ú僮鳎殉朔ú僮鬓D(zhuǎn)變?yōu)槲灰撇僮骰蚣侔l(fā)操作)
8086中有代碼寄存器CS,數(shù)據(jù)寄存器DS,堆棧寄存器SS
磁盤制造商都是采用十進(jìn)制數(shù)而不是二進(jìn)制數(shù)來表示磁盤的容量
billion和trillion在美語和英語中的意義不一樣,美語中分別是十億和一萬億,英語中是一萬億和100億億
/usr/ucb/pagesize可查看系統(tǒng)中頁面大小,頁就是操作系統(tǒng)在磁盤和內(nèi)存之間移來移去或進(jìn)行保護(hù)的單位。
用于管理內(nèi)存的調(diào)用是:
malloc 和 free —— 從堆中獲取內(nèi)存以及把內(nèi)存返回給堆brk 和 sbrk —— 調(diào)用數(shù)據(jù)段的大小至一個絕對值堆經(jīng)常出現(xiàn)的問題:
釋放或改寫仍在使用的內(nèi)存(稱為“內(nèi)存損壞”)未釋放不再使用的內(nèi)存(稱為“內(nèi)存泄漏”)檢測內(nèi)存泄漏:
netstat, vmstat查看swap -s查看交換空間大小ps -lu 用戶名 顯示所有進(jìn)程大小,其中SZ表示的是進(jìn)程頁面數(shù)使用第三方的malloc庫程序運(yùn)行時的常見錯誤:
bus error (core dumped) 總線錯誤(信息已轉(zhuǎn)儲)總線錯誤基本是由于未對齊的讀或?qū)懖僮饕鸬摹#ǔ霈F(xiàn)未對齊的內(nèi)存訪問請求時,被堵塞的組件是地址總線)對齊, alignment, 數(shù)據(jù)項(xiàng)只能存儲在地址是數(shù)據(jù)項(xiàng)大小的整數(shù)倍的內(nèi)存位置上只要對齊了,就能保證一個原子數(shù)據(jù)想不會跨越一個頁或Cache塊的邊界
union { char a[10]; int i; } u; int *p = (int *)&(u.a[1]); *p = 17; // p中未對齊的地址會引起一個總線錯誤!segmentation fault (coure dumped) 段錯誤
段錯誤或段違規(guī)(segmentation violation)是由于內(nèi)存管理單元的異常導(dǎo)致,而該異常通常是由于解除引用一個未初始化或非法值的指針引起的如果未初始化的指針恰好有未對齊的值,它將產(chǎn)生總線錯誤而不是段錯誤導(dǎo)致段錯誤的直接原因 解除引用一個包含非法值的指針解除引用一個空指針未得到正確的權(quán)限時進(jìn)行訪問。如往只讀的文本段存儲值就會引起段錯誤用完了堆棧或棧空間導(dǎo)致段錯誤的常見編程錯誤 壞指針值錯誤(指針賦值前就被引用了;向庫函數(shù)傳遞一個壞指針;對指針進(jìn)行釋放后再訪問其內(nèi)容),free(p); p = NULL; 這樣,在指針釋放后繼續(xù)使用該指針,至少程序能在終止前進(jìn)行core dump改寫錯誤(overwrite) :越過數(shù)組邊界謝姑數(shù)據(jù),在動態(tài)分配的內(nèi)存兩端寫入數(shù)據(jù),改寫一些堆管理數(shù)據(jù)結(jié)構(gòu)指針釋放引起的錯誤:釋放一個內(nèi)存塊兩次,釋放一塊未曾使用malloc分配的內(nèi)存,釋放仍在使用的內(nèi)存; 循環(huán)釋放鏈表就容易出現(xiàn)這種情況缺省情況下,會進(jìn)行信息轉(zhuǎn)儲,當(dāng)然也可以這是特定的信號處理程序(signal handler)
系統(tǒng)不支持在信號處理程序內(nèi)部調(diào)用庫函數(shù)(除非嚴(yán)格符合標(biāo)準(zhǔn)所限制的條件)“core dump” 來源于,以前所有的內(nèi)存是用鐵氧化物圓環(huán)(也就是core,指磁芯)制造的。
limit stacksize 10 可在C-shell中調(diào)整堆棧的大小
dbx工具可用來查看段錯誤等信息
根據(jù)位模式構(gòu)筑圖形 一個優(yōu)雅的#define定義
類型提升:char, bit-filed, enum , unsigned char, short, unsigned short 在表達(dá)式中,其會提升為int(前提是int能完整地容納原先的數(shù)據(jù));參數(shù)也會被提升
如果使用了原型,缺省參數(shù)提升就不會發(fā)生;如果參數(shù)聲明為char,則實(shí)際傳入的也是char
ANSI C 函數(shù)原型的目的就是使C語言稱為一種更加可靠的語言,消除型參和實(shí)參之間類型不匹配的問題。(但其并沒有排他的使用K&R C 函數(shù)原型)參數(shù)提升是為了簡化編譯器,老式的編譯器僅接受int, double 和指針類型的參數(shù)不需要按回車就能得到一個字符
C編譯器中的getch(), getche()操作系統(tǒng)發(fā)送“字符已經(jīng)就緒”的信號后,需要捕捉的信號是SIGPOLL調(diào)用庫函數(shù)之后檢查errno是個好的習(xí)慣
C 語言實(shí)現(xiàn)有限狀態(tài)機(jī) FSM
不使用臨時變量交換兩個值(兩種方法)
a ^= b;b ^= a;a ^= b;怎樣檢測到鏈表中存在循環(huán)
C語言中不同增值語句的區(qū)別(考慮變量和指針等多種情況)
x = x + 1;++x;x++;x += 1;庫函數(shù)調(diào)用和系統(tǒng)調(diào)用的區(qū)別
典型的C函數(shù)庫:system, fprintf, malloc典型的系統(tǒng)調(diào)用:fork, chdir, write, brk函數(shù)庫調(diào)用:在所有ANSI C編譯器版本中,C函數(shù)庫是相同的;語言或應(yīng)用程序的一部分;用戶地址空間執(zhí)行;調(diào)用函數(shù)庫中的一個程序;運(yùn)行時間屬于用戶時間;屬于過程調(diào)用,開銷較小;C函數(shù)庫libc中大約300個程序;記錄于unix手冊第三節(jié)系統(tǒng)調(diào)用:各個操作系統(tǒng)的系統(tǒng)調(diào)用不相同;操作系統(tǒng)的一部分;內(nèi)核地址空間執(zhí)行;調(diào)用系統(tǒng)內(nèi)核的服務(wù);運(yùn)行時間屬于系統(tǒng)時間;需要內(nèi)核上下文切換,開銷較大;在UNIX中大約有90個系統(tǒng)調(diào)用文件描述符和文件指針有何不同
文件描述符(用于索引開放文件的每個進(jìn)程表 per-process table-of-open-files)就是開放文件的每個進(jìn)程表的一個偏移量(如3),其用于unix系統(tǒng)調(diào)用中,用于標(biāo)識文件FILE 指針保存了一個FILE結(jié)構(gòu)的地址。FILE結(jié)構(gòu)用于表示開放的I/O流。它用于ANSI C標(biāo)準(zhǔn)I/O庫中,用于標(biāo)識文件 (stdio.h),結(jié)構(gòu)內(nèi)容依平臺而不一樣,在unix中通常是開放文件的每個進(jìn)程表的一個條目!編寫代碼,確定一個變量是有符號數(shù)還是無符號數(shù)
如下代碼能適用于K&R C,但由于類型提升無法適用于ANSI C
#define ISUNSIGNED(a) (a >= 0 && ~a >= 0)或#define ISUNSIGNED(type) ((type)0 - 1 > 0)打印一顆二叉樹的時間復(fù)雜度是多少?
如果execvc 系統(tǒng)調(diào)用成功,它將返回什么?(失敗才會返回)從文件中隨機(jī)提取一個字符串
對字符串進(jìn)行計(jì)數(shù),并記錄每個字符串的偏移位置如果只能順序遍歷文件一次,且不能實(shí)用表格來存儲字符串的偏移位置,怎么計(jì)算?(提示:應(yīng)用概率)如何用氣壓計(jì)測量建筑物的高度
新聞熱點(diǎn)
疑難解答