一切工作程序員自己負(fù)責(zé)。
語(yǔ)言中的所有特性都不需要隱式的運(yùn)行時(shí)支持。
程序員所做的都是對(duì)的。
程序員應(yīng)該知道自己在干什么,并保證自己的所作所為是正確的。
小即是美。事物發(fā)展都有個(gè)過(guò)程,由簡(jiǎn)入繁,不能一開(kāi)始就想得太復(fù)雜,Multics, IBM的OS/360都是因此而失敗。
C語(yǔ)言的許多特性是為了方便編譯器設(shè)計(jì)者而建立的。----唉,怎么這個(gè)樣子
C語(yǔ)言的基本數(shù)據(jù)類型直接與底層硬件相對(duì)應(yīng)。----確實(shí)如此
register關(guān)鍵字,這個(gè)設(shè)計(jì)可以說(shuō)是一個(gè)失誤,如果讓編譯器在使用各個(gè)變量時(shí)自動(dòng)處理寄存器的分配工作,顯然比一經(jīng)聲明就把這類變量在生命周期內(nèi)始終保留在寄存器里要好,使用register關(guān)鍵字,簡(jiǎn)化了編譯器,卻把包袱丟給了程序員。
C編譯器不曾實(shí)現(xiàn)的一些功能必須通過(guò)其他途徑實(shí)現(xiàn)----標(biāo)準(zhǔn)I/O庫(kù)和C預(yù)處理器。
在宏擴(kuò)展中,空格會(huì)對(duì)擴(kuò)展的結(jié)果造成很大的影響。宏后面不可加';',它不是C語(yǔ)句。宏最好只用于命名常量,并為一些適當(dāng)?shù)慕Y(jié)構(gòu)提供簡(jiǎn)捷的記法。宏名應(yīng)該大寫(xiě)這樣便很容易與函數(shù)調(diào)用區(qū)分開(kāi)來(lái)。
const關(guān)鍵字原先如果命名為readonly就好多了。
const int *p;是指不能夠通過(guò)通過(guò)p來(lái)改變int的值,即:*p = 30和p[3] = 4都是錯(cuò)誤,但p是可以改變。
const int *與int *是相容的,都是指向int的指針;const int **與int **不相容,前者是指向const int *的指針,int **是指向int *的指針。
盡量不要在你的代碼中使用無(wú)符號(hào)類型,以免增加不必要的復(fù)雜性。只有在使用位段和二進(jìn)制掩碼時(shí),才可以使用無(wú)符號(hào)數(shù)。應(yīng)該在表達(dá)式中使用強(qiáng)制類型轉(zhuǎn)換,使操作數(shù)均為有符號(hào)數(shù),或者無(wú)符號(hào)數(shù),這樣就不必由編譯器來(lái)選擇結(jié)果的類型。有個(gè)例子,在ANSI C中,-1 < (unsigned char)1為真,而-1 < (unsigned int)1 為假。
進(jìn)步——是計(jì)算機(jī)軟件工程和編程語(yǔ)言設(shè)計(jì)藝術(shù)逐步發(fā)展的重要?jiǎng)右颉_@也是為什么C++語(yǔ)言令人失望的原因:它對(duì)C語(yǔ)言中存在的一些最基本問(wèn)題沒(méi)有什么改進(jìn),而它對(duì)C語(yǔ)言最重要的擴(kuò)展(類)卻是建立在脆弱的C類型模型上。
按照C語(yǔ)言的理念,程序員應(yīng)該知道自己在干什么,而且保證自己的所作所為是正確的。
多做之過(guò):fall through作為switch的默認(rèn)行為是個(gè)失誤;相鄰的字符串自動(dòng)合并成一個(gè)字符串;太多的缺省可見(jiàn)性,全局可見(jiàn),一個(gè)大型函數(shù)一群“內(nèi)部”函數(shù)不得不在該函數(shù)的外部進(jìn)行定義。沒(méi)有人會(huì)記得在它們之前加上static限定符,所以他們?cè)谌笔∏闆r下是全局可見(jiàn)的。
誤做之過(guò):
C語(yǔ)言中符號(hào)重載:static 在函數(shù)內(nèi)部,表示該變量的值在各個(gè)調(diào)用間一直保持延續(xù)性;在函數(shù)這一極,表示該函數(shù)只對(duì)文本文件可見(jiàn)。extern用于函數(shù)定義表示全局可見(jiàn)(屬于冗余),用于變量,表示它在其他地方定義。
運(yùn)算符優(yōu)先級(jí)存在的問(wèn)題:.優(yōu)先級(jí)高于*, p.f表示(p.f);函數(shù)()高于*;==和!=高于位運(yùn)算符(val & mask != 0)表示val & (mask != 0);==和!=高于賦值符,c = getchar() != EOF表示c = (getchar() != EOF);算數(shù)運(yùn)算符高于移位運(yùn)算符 msb<<4 + lsb表示msb<<(4+lsb);逗號(hào)最低。
有些專家建議在C語(yǔ)言中記牢兩個(gè)優(yōu)先級(jí)就夠了:乘除先于加減,在涉及其他的操作符時(shí)一律加括號(hào)。
結(jié)合性,在幾個(gè)操作符具有相同優(yōu)先級(jí)時(shí)決定先執(zhí)行哪一個(gè)。
計(jì)算的次序之所以未定義,是想讓編譯器充分利用自身架構(gòu)的特點(diǎn),或者充分利用存儲(chǔ)于寄存器的值。
如果對(duì)于堆棧的每次訪問(wèn)之前都要檢查其大小和訪問(wèn)權(quán)限,對(duì)于軟件來(lái)說(shuō)代價(jià)太大了,根本不可行。
gets(char *s)
,不檢查緩沖區(qū)的空間,而fgets(char *s, int n, FILE *stream)
可以對(duì)讀入的字符數(shù)設(shè)置一個(gè)上限n。fgets對(duì)緩沖大小進(jìn)行限制的方式,更為安全。
少錯(cuò)之過(guò),標(biāo)準(zhǔn)參數(shù)的處理以及把lint程序錯(cuò)誤的從編譯器中分離出來(lái)。
Lint Early, Lint Often Lint is your software conscience. It tells you when you are doing bad things. Always use lint. Listen to your conscience.gcc as lint,使用-Wall:enable a bunch of warning。gcc --help=warning查詢。
linux上可以使用splint。
讓充滿Bug的代碼快速通過(guò)編譯實(shí)在是不劃算。----我習(xí)慣于寫(xiě)過(guò)代碼后用眼睛看一遍,確認(rèn)無(wú)誤后再編譯調(diào)試,看來(lái)以后可以在中間加上一步用lint檢查。
大型緩沖區(qū)如果閑置不用是非常浪費(fèi)空間的。
如果程序員可以在同一代碼塊中同時(shí)進(jìn)行malloc和free操作,內(nèi)存管理是最輕松的。
深刻教訓(xùn):即使可以保證你的編程語(yǔ)言100%可靠,你仍然可能成為算法中災(zāi)難的犧牲品。----確實(shí)如此,學(xué)好算法。
聲明器(declarator), 就是標(biāo)識(shí)符以及與它組合與它組合在一起的任何指針,函數(shù)括號(hào),數(shù)組下標(biāo)等。以下形式:標(biāo)識(shí)符
或標(biāo)識(shí)符[下標(biāo)]
或標(biāo)識(shí)符(參數(shù))
或(聲明器)
----注意括號(hào)不能亂加,就兩個(gè)地方可以加括號(hào)
聲明格式:類型說(shuō)明符 聲明器[,聲明器];
類型說(shuō)明符: int char void等
存儲(chǔ)類型: extern static register auto
類型限定符: const volatile
理解C語(yǔ)言聲明的優(yōu)先級(jí)規(guī)則
A聲明從它的名字開(kāi)始讀取,然后按照優(yōu)先級(jí)順序依次讀取。
B優(yōu)先級(jí)從高到底依次是:
B.1 聲明中被括號(hào)括起來(lái)的那部分
B.2 后綴操作符:
括()表示一個(gè)函數(shù),
[]表示這是一個(gè)數(shù)組。
B.3 前綴操作符:
*表示指向...的指針
C如果const和(或)volatile關(guān)鍵字與類型說(shuō)明符(如int,long等)相鄰,它作用于類型說(shuō)明符;其他情況下const和(或)volatile關(guān)鍵字作用于它左邊緊鄰的指針*號(hào)。
用優(yōu)先級(jí)規(guī)則分析C語(yǔ)言聲明:
char * const *(*next)();
char *(* c[10])(int **p);
如果需要頻繁地對(duì)整個(gè)數(shù)組進(jìn)行賦值操作,可以通過(guò)把它放入struct中。
在調(diào)用函數(shù)中,參數(shù)傳遞時(shí)首先盡可能地存放到寄存器中(追求速度)。
union也可以把同一個(gè)數(shù)據(jù)解釋成兩種不同的東西,不用強(qiáng)制類型轉(zhuǎn)換。
typedef和宏文本替換之間存在一個(gè)關(guān)鍵性的區(qū)別:typedef看成是一種徹底的'封裝'類型——在它聲明后不能再往里面增加別的東西。首先,可以用其他類型說(shuō)明符對(duì)宏類型名進(jìn)行擴(kuò)展,但對(duì)typedef所定義的類型名稱不能這樣做。typedef int banana; unsigned banana i; /*錯(cuò)誤!非法 */;其次連續(xù)幾個(gè)變量聲明。
----由于typedef由編譯器解釋的,而宏是由預(yù)處理器解釋的
typedef void (*ptr_to_func)(int);
//這樣來(lái)定義函數(shù)指針的別名。
不要為了方便起見(jiàn)對(duì)結(jié)構(gòu)使用typedef,這樣做唯一的好處是能使你不必書(shū)寫(xiě)struct關(guān)鍵字,但這個(gè)關(guān)鍵字可以向你提示一些信息。
應(yīng)該始終在struct的定義中使用結(jié)構(gòu)標(biāo)簽,即使它并非必須。這種做法可以使代碼更為清晰。結(jié)構(gòu)標(biāo)簽的名字可以取一個(gè)以'_tag'結(jié)尾的名字。
C語(yǔ)言中存在多種名字空間:
在同一個(gè)名字空間,任何名字必須具有唯一性。
----C中也有名字空間,沒(méi)注意啊。
extern對(duì)象聲明告訴編譯器對(duì)象的類型和名字,對(duì)象的內(nèi)存分配則在別處進(jìn)行。
X = Y;
在這個(gè)上下文環(huán)境里,符號(hào)X的含義是X所代表的地址。這被成為左值。
在這個(gè)上下文環(huán)境里,符號(hào)Y的含義是Y所代表的地址的內(nèi)容。這被稱為右值。
左值在編譯時(shí)可知,左值表示存儲(chǔ)結(jié)果的地方。
右值直到運(yùn)行時(shí)才知。如無(wú)特別說(shuō)明,Y的值是指右值。
數(shù)組名是個(gè)左值,但不是可修改的左值。
指針是間接尋址,數(shù)組名是直接尋址,這就是兩者在訪問(wèn)數(shù)據(jù)時(shí)的區(qū)別。指針的值是運(yùn)行時(shí)從內(nèi)存取得的,數(shù)名的值是編譯時(shí)已經(jīng)確定的。
專業(yè)的C程序員必須熟練的掌握malloc()函數(shù),并且學(xué)會(huì)用指針操縱匿名內(nèi)存。
動(dòng)態(tài)鏈接優(yōu)點(diǎn):
只使用動(dòng)態(tài)鏈接。
gcc創(chuàng)建動(dòng)態(tài)鏈接庫(kù)和使用
創(chuàng)建:gcc tomato.c -fPIC -shared -o libfruit.so
使用:gcc test.c -Wl,--rpath,. -L. -lfruit
這樣只要a.out和libfruit.so放在同一個(gè)目錄就可以了
與位置無(wú)關(guān)的代碼(position-independent code),對(duì)于共享庫(kù)顯得格外有用,因?yàn)槊總€(gè)使用共享庫(kù)的進(jìn)程一般都會(huì)把它映射到不同的虛擬地址(盡管共享同一份物理拷貝),只要修改一下偏移量表就可以了。
grep很有用啊!
始終將-l函數(shù)庫(kù)選項(xiàng)放在編譯命令行的最右邊。
警惕Interpositoning。缺省全局作用域。
準(zhǔn)則:不要讓程序中的任何符號(hào)成為全局的,除非有意把他們作為程序的接口之一。
ldd程序print shared library dependencies。
編程語(yǔ)言理論的經(jīng)典對(duì)立之一就是代碼和數(shù)據(jù)的區(qū)別。
代碼和數(shù)據(jù)的區(qū)別也可以是編譯時(shí)和運(yùn)行時(shí)的分界線。編譯器的絕大部分工作都跟翻譯代碼有關(guān);必要的數(shù)據(jù)存儲(chǔ)管理的絕大部分都在運(yùn)行時(shí)進(jìn)行。
linux可執(zhí)行文件用文件第一個(gè)字節(jié)來(lái)標(biāo)注,7F開(kāi)頭,緊跟后面的是'ELF',代表Executable and Linking Format.
可執(zhí)行文件由文本段、數(shù)據(jù)段和bss段組成,運(yùn)行size a.out可查看各段大小。
bss段保存沒(méi)有值的變量,事實(shí)上只是,給出了運(yùn)行時(shí)所需要的bss段大小。
運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)有好幾種:堆棧,過(guò)程活動(dòng)記錄,數(shù)據(jù),堆等。
堆棧有3個(gè)用處:
堆棧為函數(shù)內(nèi)部聲明的局部變量提供存儲(chǔ)空間。
進(jìn)行函數(shù)調(diào)用時(shí),堆棧存儲(chǔ)與此有關(guān)的一些維護(hù)信息。
堆棧也可以被看作暫時(shí)存儲(chǔ)區(qū)。比如計(jì)算表達(dá)式,存儲(chǔ)中間結(jié)果。
alloca()函數(shù)分配的內(nèi)存位于堆棧中,函數(shù)結(jié)束后自動(dòng)銷毀。
發(fā)現(xiàn)數(shù)據(jù)段和文本段的位置,以及位于數(shù)據(jù)段中的堆,方法是聲明位于這些段的變量,并打印它們的地址。
過(guò)程活動(dòng)記錄:局部變量,參數(shù),指向先前結(jié)構(gòu)的指針,返回地址。
Fedora上測(cè)了下,一個(gè)只有一個(gè)int參數(shù)的函數(shù)調(diào)用,要用32個(gè)字節(jié),參數(shù)4個(gè),返回地址4,esp和ebp其他不知道。fame.h中是匯編,沒(méi)太看懂。
編譯器的設(shè)計(jì)者會(huì)盡可能地把過(guò)程活動(dòng)記錄的內(nèi)容放到寄存器中,這樣可以提高速度。
static變量保存在數(shù)據(jù)段,而不是堆棧中。
auto關(guān)鍵字幾乎沒(méi)什么用處,因?yàn)樗荒苡糜诤瘮?shù)內(nèi)部,但是在函數(shù)內(nèi)部聲明的數(shù)據(jù)缺省就是這種分配。
setjmp和longjmp,在C++中變異為更普通的異常處理機(jī)制“catch”和“throw”。
對(duì)于如何在進(jìn)程中支持不同的控制線程,只要簡(jiǎn)單地為每個(gè)控制線程分配不同的堆棧即可。
indent 代碼縮進(jìn)工具
默認(rèn)GNU風(fēng)格,使用-kr選項(xiàng)按K&R風(fēng)格。還有各種各樣選項(xiàng),可以定制。
語(yǔ)法: indent [選項(xiàng)] [源文件列表]
indent [選項(xiàng)] [源文件] [-o 輸出文件]
ldd 用來(lái)查看程式運(yùn)行所需的共享庫(kù),常用來(lái)解決程式因缺少某個(gè)庫(kù)文件而不能運(yùn)行的一些問(wèn)題。
nm 打印目標(biāo)文件的符號(hào)表。
strace 工具trace system calls and signals
用法:strace [選項(xiàng)] command
gdb---哈哈,常用
time顯示程序所使用的實(shí)際時(shí)間和CPU時(shí)間
gprof列出程序的運(yùn)行時(shí)分析圖。
標(biāo)準(zhǔn)的代碼優(yōu)化技巧包括:消除循環(huán),函數(shù)代碼就地?cái)U(kuò)展,公共子表達(dá)式消除,改進(jìn)寄存器分配,省略運(yùn)行時(shí)對(duì)數(shù)組邊界的檢查,循環(huán)不變量代碼移動(dòng),操作符長(zhǎng)度削減(把指數(shù)操作轉(zhuǎn)變?yōu)槌朔ú僮鳎殉朔ú僮鬓D(zhuǎn)變?yōu)橐莆徊僮骰蚣臃ú僮鳎┑取?/p>
內(nèi)存泄漏(leak)檢查工具:
mtrace
valgrind
malloc所分配的內(nèi)存通常會(huì)圓乘為下一個(gè)大于申請(qǐng)數(shù)的2的整數(shù)次方。
總線錯(cuò)誤,幾乎都是由于未對(duì)齊的讀或?qū)懸鸬摹?---目前l(fā)inux好像不出現(xiàn)錯(cuò)誤
段錯(cuò)誤是由于MMU(內(nèi)存管理單元,負(fù)責(zé)支持虛擬內(nèi)存的硬件)的異常所致,而該異常通常是由于解除引用(查看指針?biāo)傅刂返膬?nèi)容)一個(gè)未初始化或非法值的指針引起的。
Keep it Simple, Stupid !
條件操作符簡(jiǎn)潔,允許我們高高興興的在一行內(nèi)寫(xiě)下代碼,而無(wú)需不必要的代碼膨脹。
最可能導(dǎo)致段錯(cuò)誤的常見(jiàn)編程錯(cuò)誤是:
很無(wú)厘頭的開(kāi)始。
類型提升:在任何表達(dá)式中,并不局限于涉及操作符和混合類型的操作數(shù)的表達(dá)式。
char, 位段, enum, unsigned char, short, unsigned char -> int
float -> double
任何數(shù)組 -> 相應(yīng)類型的指針。----注意
函數(shù)的參數(shù)也是表達(dá)式,所以也會(huì)發(fā)生類型提升。不用函數(shù)原型,會(huì)先提升再自動(dòng)剪裁。
如果使用了函數(shù)原型,缺省參數(shù)提升就不會(huì)發(fā)生,與實(shí)際類型相符合。----但數(shù)組到指針的提升仍會(huì)發(fā)生
不需要按回車鍵就能得到一個(gè)字符,單字符I/O----用于游戲編程,這個(gè)我就不看了
有限自動(dòng)機(jī)(FSM)可以用作程序的控制結(jié)構(gòu)。它的基本思路是用一張表保存所有可能的狀態(tài),并列出進(jìn)入每個(gè)狀態(tài)時(shí)可能執(zhí)行的所有動(dòng)作,其中最后一個(gè)動(dòng)作就是計(jì)算(通常在當(dāng)前狀態(tài)和下一次輸入字符的基礎(chǔ)上,另外再經(jīng)過(guò)一次表查詢)下一個(gè)應(yīng)該進(jìn)入的狀態(tài)。你從一個(gè)“初始狀態(tài)”開(kāi)始。在這一過(guò)程中,翻譯表可能告訴你進(jìn)入了一個(gè)錯(cuò)誤的狀態(tài),表示一個(gè)預(yù)期之外的或錯(cuò)誤的輸入。你不停地在各種狀態(tài)間轉(zhuǎn)換,直到到達(dá)結(jié)束狀態(tài)。
在C語(yǔ)言中,有好幾種方法可以用來(lái)表達(dá)FSM,但他們絕大多數(shù)都是基于函數(shù)指針數(shù)組。一個(gè)函數(shù)指針數(shù)組可以像下面這樣聲明:
void (*state)MAX_STATES;
debugging hooks
調(diào)試器調(diào)試時(shí)可以調(diào)用函數(shù),比如gdb用call 函數(shù)名,對(duì)于復(fù)雜的數(shù)據(jù)結(jié)構(gòu)可以編寫(xiě)一個(gè)函數(shù),用于遍歷數(shù)據(jù)結(jié)構(gòu)并打印出來(lái)。----時(shí)過(guò)境遷,現(xiàn)在強(qiáng)大的GUI調(diào)試器,這個(gè)已不怎么有用了。
可調(diào)式性編碼
可調(diào)式性編碼意味著把系統(tǒng)分成幾個(gè)部分,先讓程序總體結(jié)構(gòu)運(yùn)行。只有基本的程序能夠運(yùn)行之后你才能為那些復(fù)雜的細(xì)節(jié)完善、性能調(diào)優(yōu)和算法優(yōu)化進(jìn)行編碼。
有時(shí)候,花點(diǎn)時(shí)間把編程問(wèn)題分解成幾個(gè)部分往往是解決它的最快辦法。----確實(shí)得花時(shí)間,動(dòng)腦筋來(lái)分解。
作者描寫(xiě)其同事,寫(xiě)散列表就是個(gè)例子啊。最初,使散列函數(shù)返回0,這樣所有元素都存儲(chǔ)于第0個(gè)位置后面的鏈表中。----這使得程序很容易調(diào)試
復(fù)雜類型轉(zhuǎn)換,先寫(xiě)一個(gè)對(duì)象的聲明,然后刪去標(biāo)識(shí)符,最后放在左面,如int (*compar)(int *)。
不加類型說(shuō)明符,聲明變量默認(rèn)是int;函數(shù)默認(rèn)返回值是int, 一般放在eax(第一個(gè)寄存器)中。int幾乎是C語(yǔ)言所有的默認(rèn)方式。應(yīng)該也是C最善于處理的數(shù)據(jù)類型。
qsort函數(shù)原型:void qsort(void *base, size_t count, size_t size, int (*compar)(const void* element1, const void *element2));
compar函數(shù)參數(shù)可以定義為(const void *)類型,這需要在compar函數(shù)內(nèi)部cast為所處理類型;也可以直接定義為所處理類型的指針,在調(diào)用qsort函數(shù)時(shí)需要將compar函數(shù)cast為(int (*)(const void *, constvoid *)
,一開(kāi)始我以為這樣不正確(因?yàn)閝sort函數(shù)內(nèi)部還是會(huì)調(diào)用compar的,這樣類型就不匹配了?。?,其實(shí)是正確的,因?yàn)檫@種類型檢查是編譯時(shí)做的(gcc 使用-c選項(xiàng)),鏈接時(shí)不做類型檢查,只要能找到那個(gè)函數(shù)名就行,運(yùn)行時(shí)取參數(shù)更不管這些東西了,是用ebp+offset直接抓來(lái)的。
數(shù)組的聲明就是數(shù)組,指針的聲明就是指針,兩者不能混淆。聲明與定義必須對(duì)應(yīng)。
對(duì)于編譯器而言,一個(gè)數(shù)組就是一個(gè)地址,一個(gè)指針就是一個(gè)地址的地址。----左值
什么時(shí)候數(shù)組和指針是相同的?
C語(yǔ)言標(biāo)準(zhǔn)對(duì)此作了如下說(shuō)明:
指針有類型限制,是因?yàn)榫幾g器需要知道對(duì)指針進(jìn)行解除引用時(shí)應(yīng)該取幾個(gè)字節(jié),以及每個(gè)下標(biāo)的步長(zhǎng)。
sizeof(數(shù)組名)結(jié)果是數(shù)組所占字節(jié)數(shù)(真正的數(shù)組,不是函數(shù)形參),由此可見(jiàn)是可以數(shù)組名包含了長(zhǎng)度信息,并可以通過(guò)sizeof取得,所以C中檢查數(shù)組是否越界訪問(wèn)是能夠做到的,但是很容易用指針避開(kāi),就像用指針可以修改const一樣。我覺(jué)得編譯器可以打開(kāi)一個(gè)選項(xiàng),是否檢查數(shù)組越界訪問(wèn)。
把作為形參的數(shù)組和指針等同起來(lái)是出于效率原因的考慮。在C語(yǔ)言中,所有非數(shù)組形式數(shù)據(jù)實(shí)參均以傳值形式。如果要copy整個(gè)數(shù)組,無(wú)論在時(shí)間上還是內(nèi)存空間上的開(kāi)銷都可能是非常大的。
int apricot[2][3][5]; // apricot 兩個(gè)[3][5]的數(shù)組,2*3個(gè)[5]的數(shù)組,2*3*5個(gè)int
int (*p)[3][5] = apricot; // 步長(zhǎng) 3 * 5
int (*r)[5] = apricot[0]; // 步長(zhǎng) 5
int *t = apricot[0][0]; // 步長(zhǎng) 1
int u = apricot[0][0][0];
指向數(shù)組第一個(gè)元素的指針與數(shù)組名等同。
內(nèi)存中數(shù)組的布局
C語(yǔ)言中,最右邊的下標(biāo)最先變化,這個(gè)約定被稱為'行主序'。
只有字符串常量才可以初始化指針數(shù)組,因?yàn)榭蓤?zhí)行文件中字符串常量是作為數(shù)據(jù)存儲(chǔ)。而161這樣的字面常量只出現(xiàn)在代碼中。
數(shù)組與指針可交換性的總結(jié):
數(shù)組和指針參數(shù)是如何被編譯器修改的?
“數(shù)組名被改寫(xiě)成一個(gè)指針參數(shù)”規(guī)則并不是遞歸定義的。數(shù)組的數(shù)組會(huì)被改寫(xiě)成“數(shù)組的指針”,而不是“指針的指針”。
數(shù)組的數(shù)組 char c[8][10]; char (*c)[10]; 數(shù)組的指針
指針數(shù)組 char *c[15]; char**c; 指針的指針
指針的指針 char **c; char **c; 不改變----指針與指針不用修改
數(shù)組的指針 char (*c)[64]; char (*c)[64]; 不改變----注意,指向一個(gè)長(zhǎng)度為64的char數(shù)組的數(shù)組名的指針,訪問(wèn)數(shù)組中元素這樣做:(*c)[0]。
int a[20];
int **p = &a; // 錯(cuò)誤,指針的指針與數(shù)組的指針不兼容
int (*t)[20] = &a; // 正確,t為由20個(gè)int的數(shù)組的指針。
----此處括號(hào)是必須的,因?yàn)閇]的優(yōu)先級(jí)比*高
Iliffe向量,創(chuàng)建一個(gè)一維數(shù)組,數(shù)組中的元素是指向其他東西的指針。
例如main(int argc, char *argv[])
,第二個(gè)參數(shù)會(huì)被改寫(xiě)成char **。(注意,只有把二維數(shù)組改為一個(gè)指向向量的指針數(shù)組的前提下才可以這么做!)
在C語(yǔ)言中,傳遞多維數(shù)組必須提供除最左面一維以外的所有維的長(zhǎng)度。
可以放棄多維數(shù)組的形式,提供自己的下標(biāo)方式,如char_array[row_size*i + j] = ...
模擬動(dòng)態(tài)數(shù)組,當(dāng)表滿后,用realloc()對(duì)數(shù)組重新分配內(nèi)存,并確保realloc操作成功。
重分配操作很可能把原先的整個(gè)內(nèi)存塊移到一個(gè)不同的位置,這樣表格中元素的地址便不再有效。為了避免麻煩,應(yīng)該使用下標(biāo)而不是元素的地址。----這也是STL中引入迭代器的一個(gè)原因吧
“增加”和“刪除”操作都必須通過(guò)函數(shù)來(lái)進(jìn)行,這樣才能維持表的完整性。
類內(nèi)部定義的函數(shù)是inline函數(shù)
重載是編譯時(shí)解析的。
多態(tài)——運(yùn)行時(shí)綁定。latebinding
new和delete操作符,用于取代malloc()和free()函數(shù),能夠自動(dòng)完成sizeof的計(jì)算工作,并會(huì)自動(dòng)調(diào)用合適的構(gòu)造函數(shù)和析構(gòu)函數(shù)。new能真正的創(chuàng)建一個(gè)對(duì)象,malloc()函數(shù)只是分配內(nèi)存。
C++的設(shè)計(jì)受限于嚴(yán)格的兼容性、內(nèi)部一致性和高效率。
復(fù)用是軟件科學(xué)的一個(gè)崇高而又朦朧的目標(biāo)。----很多時(shí)候不如另起爐灶從頭開(kāi)始
管理和市場(chǎng)狀況是導(dǎo)致許多公司破產(chǎn)的原因,比單純的技術(shù)失敗更為常見(jiàn)。那些不時(shí)刻注意顧客需求的公司終究難以為繼,最能掌握這項(xiàng)藝術(shù)的公司往往能獲得成功。
面試的關(guān)鍵在于正確理解問(wèn)題!你需要仔細(xì)地聽(tīng),如果不理解問(wèn)題或者覺(jué)得它的定義不清,可以要求一個(gè)更好的解釋。
提供一種尋找可靠答案的好方法。
鏈表環(huán)的檢測(cè)。
mango[i++] += y; // i++僅執(zhí)行一次
優(yōu)秀的程序員將會(huì)休息的更好,精力更加充沛,而蹩腳的程序員則很可能困得腦袋常常和桌子打架。
人類的最高目標(biāo)是奮斗、尋求、創(chuàng)造。
聯(lián)系客服