當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 嵌入式云IOT技術(shù)圈
[導(dǎo)讀]C語(yǔ)言的設(shè)計(jì)哲學(xué): 一切工作程序員自己負(fù)責(zé)。 語(yǔ)言中的所有特性都不需要隱式的運(yùn)行時(shí)支持。 程序員所做的都是對(duì)的。 程序員應(yīng)該知道自己在干什么,并保證自己的所作所為是正確的。 第1章-- C: 穿越時(shí)空的迷霧 小即是美。事物發(fā)展都有個(gè)過(guò)程,由簡(jiǎn)入繁,不能一

C語(yǔ)言的設(shè)計(jì)哲學(xué):

一切工作程序員自己負(fù)責(zé)。
語(yǔ)言中的所有特性都不需要隱式的運(yùn)行時(shí)支持。
程序員所做的都是對(duì)的。
程序員應(yīng)該知道自己在干什么,并保證自己的所作所為是正確的。

第1章-- C: 穿越時(shí)空的迷霧

小即是美。事物發(fā)展都有個(gè)過(guò)程,由簡(jiǎn)入繁,不能一開始就想得太復(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)該大寫這樣便很容易與函數(shù)調(diào)用區(qū)分開來(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 為假。

第2章-- 這不是Bug,而是語(yǔ)言特性

進(jìn)步——是計(jì)算機(jī)軟件工程和編程語(yǔ)言設(shè)計(jì)藝術(shù)逐步發(fā)展的重要?jiǎng)右?。這也是為什么C++語(yǔ)言令人失望的原因:它對(duì)C語(yǔ)言中存在的一些最基本問(wèn)題沒有什么改進(jìn),而它對(duì)C語(yǔ)言最重要的擴(kuò)展(類)卻是建立在脆弱的C類型模型上。

按照C語(yǔ)言的理念,程序員應(yīng)該知道自己在干什么,而且保證自己的所作所為是正確的。

多做之過(guò):fall through作為switch的默認(rèn)行為是個(gè)失誤;相鄰的字符串自動(dòng)合并成一個(gè)字符串;太多的缺省可見性,全局可見,一個(gè)大型函數(shù)一群“內(nèi)部”函數(shù)不得不在該函數(shù)的外部進(jìn)行定義。沒有人會(huì)記得在它們之前加上static限定符,所以他們?cè)谌笔∏闆r下是全局可見的。

誤做之過(guò):

C語(yǔ)言中符號(hào)重載:static 在函數(shù)內(nèi)部,表示該變量的值在各個(gè)調(diào)用間一直保持延續(xù)性;在函數(shù)這一極,表示該函數(shù)只對(duì)文本文件可見。extern用于函數(shù)定義表示全局可見(屬于冗余),用于變量,表示它在其他地方定義。

運(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í)慣于寫過(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é)好算法。

第3章-- 分析C語(yǔ)言的聲明

聲明器(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 聲明從它的名字開始讀取,然后按照優(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ù)指針的別名。

不要為了方便起見對(duì)結(jié)構(gòu)使用typedef,這樣做唯一的好處是能使你不必書寫struct關(guān)鍵字,但這個(gè)關(guān)鍵字可以向你提示一些信息。

應(yīng)該始終在struct的定義中使用結(jié)構(gòu)標(biāo)簽,即使它并非必須。這種做法可以使代碼更為清晰。結(jié)構(gòu)標(biāo)簽的名字可以取一個(gè)以"_tag"結(jié)尾的名字。

C語(yǔ)言中存在多種名字空間:

  • 標(biāo)簽名(label name)
  • 標(biāo)簽(tag): 這個(gè)名字空間用于所有的結(jié)構(gòu)、枚舉和聯(lián)合
  • 成員名:每個(gè)結(jié)構(gòu)或聯(lián)合都有自身的名字空間
  • 其他

在同一個(gè)名字空間,任何名字必須具有唯一性。

----C中也有名字空間,沒注意啊。

第4章-- 令人震驚的事實(shí):數(shù)組和指針并不相同

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)存。

第5章-- 對(duì)鏈接的思考

動(dòng)態(tài)鏈接優(yōu)點(diǎn):

  • 1.可執(zhí)行文件的體積小,節(jié)省磁盤空間和虛擬內(nèi)存。
  • 2.所有動(dòng)態(tài)鏈接到某個(gè)特定函數(shù)庫(kù)的可執(zhí)行文件在運(yùn)行時(shí)共享該函數(shù)庫(kù)在內(nèi)存中的一個(gè)單獨(dú)拷貝。

只使用動(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很有用?。?/p>

始終將-l函數(shù)庫(kù)選項(xiàng)放在編譯命令行的最右邊。

警惕Interpositoning。缺省全局作用域。

準(zhǔn)則:不要讓程序中的任何符號(hào)成為全局的,除非有意把他們作為程序的接口之一。

ldd程序print shared library dependencies。

第6章-- 運(yùn)動(dòng)的詩(shī)章:運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)

編程語(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開頭,緊跟后面的是"ELF",代表Executable and Linking Format.

可執(zhí)行文件由文本段、數(shù)據(jù)段和bss段組成,運(yùn)行size a.out可查看各段大小。

bss段保存沒有值的變量,事實(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中是匯編,沒太看懂。

編譯器的設(shè)計(jì)者會(huì)盡可能地把過(guò)程活動(dòng)記錄的內(nèi)容放到寄存器中,這樣可以提高速度。

static變量保存在數(shù)據(jù)段,而不是堆棧中。

auto關(guān)鍵字幾乎沒什么用處,因?yàn)樗荒苡糜诤瘮?shù)內(nèi)部,但是在函數(shù)內(nèi)部聲明的數(shù)據(jù)缺省就是這種分配。

setjmp和longjmp,在C++中變異為更普通的異常處理機(jī)制“catch”和“throw”。

對(duì)于如何在進(jìn)程中支持不同的控制線程,只要簡(jiǎn)單地為每個(gè)控制線程分配不同的堆棧即可。

有用的C語(yǔ)言工具:

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)槌朔ú僮?,把乘法操作轉(zhuǎn)變?yōu)橐莆徊僮骰蚣臃ú僮鳎┑取?/p>

第7章-- 對(duì)內(nèi)存的思考

內(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)寫下代碼,而無(wú)需不必要的代碼膨脹。

最可能導(dǎo)致段錯(cuò)誤的常見編程錯(cuò)誤是:


  1. 壞指針的錯(cuò)誤。free(p);后值空 p = NULL;

    1. 改寫錯(cuò)誤。如數(shù)組越界。

    1. 指針釋放引起的錯(cuò)誤。

    第8章-- 為什么程序員無(wú)法分清萬(wàn)圣節(jié)和圣誕節(jié)

    很無(wú)厘頭的開始。

    類型提升:在任何表達(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)”開始。在這一過(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)可以編寫一個(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)分解。

    作者描寫其同事,寫散列表就是個(gè)例子啊。最初,使散列函數(shù)返回0,這樣所有元素都存儲(chǔ)于第0個(gè)位置后面的鏈表中。----這使得程序很容易調(diào)試

    復(fù)雜類型轉(zhuǎn)換,先寫一個(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 *),一開始我以為這樣不正確(因?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)的。

    第9章-- 再論數(shù)組

    數(shù)組的聲明就是數(shù)組,指針的聲明就是指針,兩者不能混淆。聲明與定義必須對(duì)應(yīng)。

    對(duì)于編譯器而言,一個(gè)數(shù)組就是一個(gè)地址,一個(gè)指針就是一個(gè)地址的地址。----左值

    什么時(shí)候數(shù)組和指針是相同的?

    C語(yǔ)言標(biāo)準(zhǔn)對(duì)此作了如下說(shuō)明:

    • 規(guī)則1. 表達(dá)式中的數(shù)組名(與聲明不同)被編譯器當(dāng)作一個(gè)指向該數(shù)組第一個(gè)元素的指針。
    • 規(guī)則2. 下標(biāo)(subscript)總是與指針的偏移(an offset from a pointer相同
    • 規(guī)則3. 在函數(shù)參數(shù)的聲明中,數(shù)組名被編譯器當(dāng)作指向該數(shù)組的第一個(gè)元素的指針----這里數(shù)組是指一維數(shù)組

    指針有類型限制,是因?yàn)榫幾g器需要知道對(duì)指針進(jìn)行解除引用時(shí)應(yīng)該取幾個(gè)字節(jié),以及每個(gè)下標(biāo)的步長(zhǎng)。

    sizeof(數(shù)組名)結(jié)果是數(shù)組所占字節(jié)數(shù)(真正的數(shù)組,不是函數(shù)形參),由此可見是可以數(shù)組名包含了長(zhǎng)度信息,并可以通過(guò)sizeof取得,所以C中檢查數(shù)組是否越界訪問(wèn)是能夠做到的,但是很容易用指針避開,就像用指針可以修改const一樣。我覺得編譯器可以打開一個(gè)選項(xiàng),是否檢查數(shù)組越界訪問(wèn)。

    把作為形參的數(shù)組和指針等同起來(lái)是出于效率原因的考慮。在C語(yǔ)言中,所有非數(shù)組形式數(shù)據(jù)實(shí)參均以傳值形式。如果要copy整個(gè)數(shù)組,無(wú)論在時(shí)間上還是內(nè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é):


    1. 用a[i]這樣的形式對(duì)數(shù)組進(jìn)行訪問(wèn)總是被編譯器”改寫“或解釋為像*(a+i)這樣的指針訪問(wèn)。

    1. 指針始終就是指針。它絕不可以改寫成數(shù)組。只是可以使用下標(biāo)形式訪問(wèn)指針。

    1. 在特定上下文中,也就是指針作為函數(shù)的參數(shù)(也就只有這種情況--注意),一個(gè)數(shù)組的聲明可以看作是一個(gè)指針。作為函數(shù)參數(shù)的數(shù)組始終會(huì)被編譯器修改成為指向數(shù)組中第一個(gè)元素的指針。

    第10章-- 再論指針

    數(shù)組和指針參數(shù)是如何被編譯器修改的?

    “數(shù)組名被改寫成一個(gè)指針參數(shù)”規(guī)則并不是遞歸定義的。數(shù)組的數(shù)組會(huì)被改寫成“數(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ì)被改寫成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)行,這樣才能維持表的完整性。

    第11章-- 你懂得C,所以C++不在話下

    類內(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í)候不如另起爐灶從頭開始

    管理和市場(chǎng)狀況是導(dǎo)致許多公司破產(chǎn)的原因,比單純的技術(shù)失敗更為常見。那些不時(shí)刻注意顧客需求的公司終究難以為繼,最能掌握這項(xiàng)藝術(shù)的公司往往能獲得成功。

    附錄A-- 程序員工作面試的秘密

    面試的關(guān)鍵在于正確理解問(wèn)題!你需要仔細(xì)地聽,如果不理解問(wèn)題或者覺得它的定義不清,可以要求一個(gè)更好的解釋。

    提供一種尋找可靠答案的好方法。

    鏈表環(huán)的檢測(cè)。

    mango[i++] += y; // i++僅執(zhí)行一次

    優(yōu)秀的程序員將會(huì)休息的更好,精力更加充沛,而蹩腳的程序員則很可能困得腦袋常常和桌子打架。

    人類的最高目標(biāo)是奮斗、尋求、創(chuàng)造。

    往期精彩

    學(xué)習(xí)嵌入式可以帶娃,不信你們看

    第10期 | ringbuff,通用FIFO環(huán)形緩沖區(qū)實(shí)現(xiàn)庫(kù)

    代碼寫得很牛逼但UI界面卻搞得很丑?來(lái),楊工帶你!

    分享一個(gè)近期開源火爆全網(wǎng)的額溫槍方案(硬件+源碼)

    火爆全網(wǎng)開源額溫槍同平臺(tái)之華大HC32L136 SDK開發(fā)入門

    覺得本次分享的文章對(duì)您有幫助,隨手點(diǎn)[在看]并轉(zhuǎn)發(fā)分享,也是對(duì)我的支持。

    免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!

    本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
    換一批
    延伸閱讀

    9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

    關(guān)鍵字: 阿維塔 塞力斯 華為

    倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

    關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

    北京2024年8月28日 /美通社/ -- 越來(lái)越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來(lái)越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

    關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

    8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

    關(guān)鍵字: 騰訊 編碼器 CPU

    8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

    關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

    8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

    關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

    要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

    關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

    北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

    關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

    北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

    關(guān)鍵字: BSP 信息技術(shù)
    關(guān)閉
    關(guān)閉