国产精品一久久香蕉产线看-国产精品一区在线播放-国产精品自线在线播放-国产毛片久久国产-一级视频在线-一级视频在线观看免费

c語言讀書筆記

時(shí)間:2022-06-24 12:50:52 讀書筆記 我要投稿
  • 相關(guān)推薦

c語言讀書筆記

C語言是一門通用計(jì)算機(jī)編程語言,應(yīng)用廣泛。C語言的設(shè)計(jì)目標(biāo)是提供一種能以簡(jiǎn)易的方式編譯、處理低級(jí)存儲(chǔ)器、產(chǎn)生少量的機(jī)器碼以及不需要任何運(yùn)行環(huán)境支持便能運(yùn)行的編程語言。品才網(wǎng)整理了C語言的讀書筆記,歡迎大家閱讀。

c語言讀書筆記

c語言讀書筆記

《C 語言深度解剖》這本書是一本“解開程序員面試筆試的秘密”的好書。作者陳正沖老師提出“以含金量勇敢挑戰(zhàn)國(guó)內(nèi)外同類書籍”,確實(shí),這本書中的知識(shí)點(diǎn)都是一些在面試中常見的考點(diǎn),并且很多都是我們平常不注意的點(diǎn),對(duì)于我們深入理解C語言確實(shí)很有幫助。

第1章關(guān)鍵字

1.register

雖然寄存器的速度非�?�,但是使用register修飾符也有些限制的:register變量必須是能被CPU寄存器所接受的類型。

意味著register變量必須是一個(gè)單個(gè)的值,并且其長(zhǎng)度應(yīng)小于或等于整型的長(zhǎng)度。而且register變量可能不存放在內(nèi)存中,

所以不能用取址運(yùn)算符“&”來獲取register變量的地址。

2.static修飾符

(1)修飾變量

靜態(tài)局部變量,在函數(shù)體里面定義的,就只能在這個(gè)函數(shù)里用了,同一個(gè)文檔中的其他函數(shù)也用不了。由于被static修飾的變量總是存在內(nèi)存的靜態(tài)區(qū),所以即使這個(gè)函數(shù)運(yùn)行結(jié)束,這個(gè)靜態(tài)變量的值還是不會(huì)被銷毀,函數(shù)下次使用時(shí)仍然能用到這個(gè)值。

(2)修飾函數(shù)

第二個(gè)作用:修飾函數(shù)。函數(shù)前加static使得函數(shù)成為靜態(tài)函數(shù)。但此處“static”的含義不是指存儲(chǔ)方式,而是指對(duì)函數(shù)的作用域僅局限于本文件(所以又稱內(nèi)部函數(shù))。使用內(nèi)部函數(shù)的好處是:不同的人編寫不同的函數(shù)時(shí),不用擔(dān)心自己定義的函數(shù),是否會(huì)與其它文件中的函數(shù)同名。

關(guān)鍵字static有著不尋常的歷史。起初,在C中引入關(guān)鍵字static是為了表示退出一個(gè)塊后仍然存在的局部變量。隨后,static在C中有了第二種含義:用來表示不能被其它文件訪問的全局變量和函數(shù)。為了避免引入新的關(guān)鍵字,所以仍使用static關(guān)鍵字來表示這第二種含義。

3.if語句使用注意

先處理正常情況,再處理異常情況。

在編寫代碼是,要使得正常情況的執(zhí)行代碼清晰,確認(rèn)那些不常發(fā)生的異常情況處理代碼不會(huì)遮掩正常的執(zhí)行路徑。這樣對(duì)于代碼的可讀性和性能都很重要。因?yàn)?if

語句總是需要做判斷,而正常情況一般比異常情況發(fā)生的概率更大(否則就應(yīng)該把異常正常調(diào)過來了),如果把執(zhí)行概率更大的代碼放到后面,也就意味著if語句將進(jìn)行多次無謂的比較。

另外,非常重要的一點(diǎn)是,把正常情況的處理放在if后面,而不要放在else后面。當(dāng)然這也符合把正常情況的處理放在前面的要求。

4.千萬小心又小心使用void指針類型。

按照ANSI(AmericanNationalStandardsInstitute)標(biāo)準(zhǔn),不能對(duì)void指針進(jìn)行算法操作,即下列操作都是不合法的:

void*pvoid;

pvoid++;//ANSI:錯(cuò)誤

pvoid+=1;//ANSI:錯(cuò)誤

ANSI標(biāo)準(zhǔn)之所以這樣認(rèn)定,是因?yàn)樗鼒?jiān)持:進(jìn)行算法操作的指針必須是確定知道其指向數(shù)據(jù)類型大小的。也就是說必須知道內(nèi)存目的地址的確切值。

例如:

int*pint;

pint++;//ANSI:正確

但是大名鼎鼎的GNU(GNU'sNotUnix的遞歸縮寫)則不這么認(rèn)定,它指定void*的算法操作與char*一致。因此下列語句在GNU編譯器中皆正確:

pvoid++;//GNU:正確

pvoid+=1;//GNU:正確

在實(shí)際的程序設(shè)計(jì)中,為符合ANSI標(biāo)準(zhǔn),并提高程序的可移植性,我們可以這樣編寫實(shí)現(xiàn)同樣功能的代碼:

void*pvoid;

(char*)pvoid++;//ANSI:正確;GNU:正確

(char*)pvoid+=1;//ANSI:錯(cuò)誤;GNU:正確

GNU和ANSI還有一些區(qū)別,總體而言,GNU較ANSI更“開放”,提供了對(duì)更多語法的支持。但是我們?cè)谡鎸?shí)設(shè)計(jì)時(shí),還是應(yīng)該盡可能地符合ANSI標(biāo)準(zhǔn)。

5.const與宏

節(jié)省空間,避免不必要的內(nèi)存分配,同時(shí)提高效率

編譯器通常不為普通const只讀變量分配存儲(chǔ)空間,而是將它們保存在符號(hào)表中,這使得它成為一個(gè)編譯期間的值,沒有了存儲(chǔ)與讀內(nèi)存的操作,使得它的效率也很高。

例如:

#define M 3//宏常量

const int N=5;//此時(shí)并未將N放入內(nèi)存中

......

inti=N;//此時(shí)為N分配內(nèi)存,以后不再分配!

intI=M;//預(yù)編譯期間進(jìn)行宏替換,分配內(nèi)存

intj=N;//沒有內(nèi)存分配

intJ=M;//再進(jìn)行宏替換,又一次分配內(nèi)存!

const定義的只讀變量從匯編的角度來看,只是給出了對(duì)應(yīng)的內(nèi)存地址,而不是象#define一樣給出的是立即數(shù),所以,const定義的只讀變量在程序運(yùn)行過程中只有一份拷貝(因?yàn)樗侨值闹蛔x變量,存放在靜態(tài)區(qū)),而#define定義的宏常量在內(nèi)存中有若干個(gè)拷貝。#define宏是在預(yù)編譯階段進(jìn)行替換,而const修飾的只讀變量是在編譯的時(shí)候確定其值。

#define宏沒有類型,而const修飾的只讀變量具有特定的類型。

6.最易變的關(guān)鍵字----volatile

volatile是易變的、不穩(wěn)定的意思。很多人根本就沒見過這個(gè)關(guān)鍵字,不知道它的存在。也有很多程序員知道它的存在,但從來沒用過它。我對(duì)它有種“楊家有女初長(zhǎng)成,養(yǎng)在深閨人未識(shí)”的感覺。volatile關(guān)鍵字和const一樣是一種類型修飾符,用它修飾的變量表示可以被某些編譯器未知的因素更改,比如操作系統(tǒng)、硬件或者其它線程等。遇到這個(gè)關(guān)鍵字聲明的變量,編譯器對(duì)訪問該變量的代碼就不再進(jìn)行優(yōu)化,從而可以提供對(duì)特殊地址的穩(wěn)定訪問。

先看看下面的例子:

int i=10;

int j=i;//(1)語句

int k=i;//(2)語句

這時(shí)候編譯器對(duì)代碼進(jìn)行優(yōu)化,因?yàn)樵?1)(2)兩條語句中,i沒有被用作左值。這時(shí)候編譯器認(rèn)為i的值沒有發(fā)生改變,所以在(1)語句時(shí)從內(nèi)存中取出i的值賦給j

之后,這個(gè)值并沒有被丟掉,而是在(2)語句時(shí)繼續(xù)用這個(gè)值給k賦值。編譯器不會(huì)生成出匯編代碼重新從內(nèi)存里取i的值,這樣提高了效率。但要注意:(1)(2)語句之間i沒有被用作左值才行。

再看另一個(gè)例子:

volatile int i=10;

int j=i;//(3)語句

int k=i;//(4)語句

volatile關(guān)鍵字告訴編譯器i是隨時(shí)可能發(fā)生變化的,每次使用它的時(shí)候必須從內(nèi)存中取出i的值,因而編譯器生成的匯編代碼會(huì)重新從i的地址處讀取數(shù)據(jù)放在k中。

這樣看來,如果i是一個(gè)寄存器變量或者表示一個(gè)端口數(shù)據(jù)或者是多個(gè)線程的共享數(shù)據(jù),就容易出錯(cuò),所以說volatile可以保證對(duì)特殊地址的穩(wěn)定訪問。

但是注意:在VC++6.0中,一般Debug模式?jīng)]有進(jìn)行代碼優(yōu)化,所以這個(gè)關(guān)鍵字的作用有可能看不出來。你可以同時(shí)生成Debug版和Release版的程序做個(gè)測(cè)試。

留一個(gè)問題:const volatile int i=10;這行代碼有沒有問題?如果沒有,那i到底是什么屬性?

這個(gè)可以同時(shí)使用。

7.空結(jié)構(gòu)體是有大小的

structstudent

{

}stu;

sizeof(stu)的值是多少呢?在VisualC++6.0上測(cè)試一下。

很遺憾,不是0,而是1。為什么呢?你想想,如果我們把structstudent看成一個(gè)模子的話,你能造出一個(gè)沒有任何容積的模子嗎?顯然不行。編譯器也是如此認(rèn)為。編譯器認(rèn)為任何一種數(shù)據(jù)類型都有其大小,用它來定義一個(gè)變量能夠分配確定大小的空間。既然如此,編譯器就理所當(dāng)然的認(rèn)為任何一個(gè)結(jié)構(gòu)體都是有大小的.,哪怕這個(gè)結(jié)構(gòu)體為空。那萬一結(jié)構(gòu)體真的為空,它的大小為什么值比較合適呢?假設(shè)結(jié)構(gòu)體內(nèi)只有一個(gè)char型的數(shù)據(jù)成員,那其大小為1byte(這里先不考慮內(nèi)存對(duì)齊的情況).也就是說非空結(jié)構(gòu)體類型數(shù)據(jù)最少需要占一個(gè)字節(jié)的空間,而空結(jié)構(gòu)體類型數(shù)據(jù)總不能比最小的非空結(jié)構(gòu)體類型數(shù)據(jù)所占的空間大吧。這就麻煩了,空結(jié)構(gòu)體的大小既不能為0,也不能大于1,怎么辦?定義為0.5個(gè)byte?但是內(nèi)存地址的最小單位是1個(gè)byte,0.5個(gè)byte怎么處理?解決這個(gè)問題的最好辦法就是折中,編譯器理所當(dāng)然的認(rèn)為你構(gòu)造一個(gè)結(jié)構(gòu)體數(shù)據(jù)類型是用來打包一些數(shù)據(jù)成員的,而最小的數(shù)據(jù)成員需要1個(gè)byte,編譯器為每個(gè)結(jié)構(gòu)體類型數(shù)據(jù)至少預(yù)留1個(gè)byte的空間。所以,空結(jié)構(gòu)體的大小就定位1個(gè)byte。

8. 大端與小端

在x86 系統(tǒng)下,輸出的值為多少?

#include

int main()

{

int a[5]={1,2,3,4,5};

int *ptr1=(int *)(&a+1);

int *ptr2=(int *)((int)a+1);

printf("%x,%x",ptr1[-1],*ptr2);

return 0;

}

5和0x02000000

由于x86是小端方式,所以低位內(nèi)容存放到了低位地址。圖中每一種顏色代筆一個(gè)int型的內(nèi)存分布。&a可以獲得數(shù)組a的地址,也就是這兒的0xbfd46624, 所以&a+1的結(jié)果應(yīng)該是0xbfd46638(即圖中最下面紅色部分)。對(duì)于代碼中的ptr1由于其為int型指針,所以ptr[-1]的意思應(yīng)該是取0xbfd46638地址之前的一個(gè)整型,即為a數(shù)組中的最后一個(gè)值5。而在計(jì)算ptr2的時(shí)候,(int)a是將整型地址a轉(zhuǎn)換成了一個(gè)整型數(shù),這樣(int)a+1的結(jié)果就是0xbfd46625,然后再將其轉(zhuǎn)化為int型指針,這樣利用ptr2獲得的數(shù)值就是從0xbfd46625開始的一個(gè)整型,即為0x02000000

10. 花括號(hào)

花括號(hào)每個(gè)人都見過,很簡(jiǎn)單吧。但曾經(jīng)有一個(gè)學(xué)生問過我如下問題:

char a[10] = {“abcde”};

他不理解為什么這個(gè)表達(dá)式正確。我讓他繼續(xù)改一下這個(gè)例子:

char a[10] { = “abcde”};

問他這樣行不行。那讀者以為呢?為什么?

花括號(hào)的作用是什么呢?我們平時(shí)寫函數(shù),if、while、for、switch 語句等都用到了它,但有時(shí)又省略掉了它。簡(jiǎn)單來說花括號(hào)的作用就是打包。你想想以前用花括號(hào)是不是為了把一些語句或代碼打個(gè)包包起來,使之形成一個(gè)整體,并與外界絕緣。這樣理解的話,上面的問題就不是問題了。

11.再論 a 和&a 之間的區(qū)別

int main()

{

char a[5]={'A','B','C','D'};

char (*p3)[5] = &a;

char (*p4)[5] = a;

return 0;

}

int main()

{

char a[5]={'A','B','C','D'};

char (*p3)[3] = &a;

char (*p4)[3] = a;

return 0;

}

int main()

{

char a[5]={'A','B','C','D'};

char (*p3)[10] = &a;

char (*p4)[10] = a;

return 0;

}

int a[5][5];

int (*p)[4];

p = a;

問&p[4][2] - &a[4][2]的值為多少?

12. 用 malloc 函數(shù)申請(qǐng) 0 字節(jié)內(nèi)存

另外還有一個(gè)問題:用 malloc 函數(shù)申請(qǐng) 0 字節(jié)內(nèi)存會(huì)返回 NULL 指針嗎?

可以測(cè)試一下,也可以去查找關(guān)于 malloc 函數(shù)的說明文檔。申請(qǐng) 0 字節(jié)內(nèi)存,函數(shù)并不返回 NULL,而是返回一個(gè)正常的內(nèi)存地址。但是你卻無法使用這塊大小為 0

的內(nèi)存。這好尺子上的某個(gè)刻度,刻度本身并沒有長(zhǎng)度,只有某兩個(gè)刻度一起才能量出長(zhǎng)度。對(duì)于這一點(diǎn)一定要小心,因?yàn)檫@時(shí)候 if(NULL != p)語句校驗(yàn)將不起作用。

13. 不使用任何變量編寫 strlen 函數(shù)

看到這里,也許有人會(huì)說,strlen 函數(shù)這么簡(jiǎn)單,有什么好討論的。是的,我相信你能 熟練應(yīng)用這個(gè)函數(shù),也相信你能輕易的寫出這個(gè)函數(shù)。但是如果我把要求提高一些呢:

不允許調(diào)用庫(kù)函數(shù),也不允許使用任何全局或局部變量編寫 int my_strlen (char *strDest); 似乎問題就沒有那么簡(jiǎn)單了吧?這個(gè)問題曾經(jīng)在網(wǎng)絡(luò)上討論的比較熱烈,我?guī)缀跏侨獭坝^戰(zhàn)” ,差點(diǎn)也忍不住手癢了。不過因?yàn)槲业慕鉀Q辦法在我看到帖子時(shí)已經(jīng)有人提出了, 所以作罷。

解決這個(gè)問題的辦法由好幾種,比如嵌套有編語言。因?yàn)榍短讌R編一般只在嵌入式底 層開發(fā)中用到,所以本書就不打算討論 C 語言嵌套匯編的知識(shí)了。 有興趣的讀者,可以查找相關(guān)資料。 也許有的讀者想到了用遞歸函數(shù)來解決這個(gè)問題。是的,你應(yīng)該想得到,因?yàn)槲野堰@ 個(gè)問題放在講解函數(shù)遞歸的時(shí)候討論。

既然已經(jīng)有了思路, 這個(gè)問題就很簡(jiǎn)單了。

代碼如下:

int my_strlen( const char* strDest )

{

assert(NULL != strDest);

if ('

主站蜘蛛池模板: 欧美极度极度另类 | 免费一级α片在线观看 | 91社区在线观看精品 | 久久久久88色偷偷 | 中文字幕一区在线播放 | 亚洲精品国产成人 | 在线观看亚洲天堂 | 狠狠躁天天躁夜夜躁夜天战 | 国产91精品不卡在线 | 日日摸处处碰夜夜爽视频 | 亚洲免费视频播放 | 999成人网| 手机在线伦理片 | 国内体内she精视频免费 | 在线日韩三级 | 国产日韩欧美亚洲综合首页 | 亚洲第一欧美 | 日韩精品久久久免费观看夜色 | 欧美日韩一二三区 | 亚洲成综合人影院在院播放 | 久久成人综合网 | 国产日韩欧美一区二区三区视频 | 播放一级特黄录像 | 在线网站污 | 亚洲福利国产 | 美妇乱人伦小说目录 | 欧美一区二区三区高清不卡tv | 轻点插视频| 欧美色图一区 | 国产亚洲欧洲国产综合一区 | 在线免费看黄视频 | 97国产成人精品视频 | 一级在线免费视频 | 涩涩在线| 波多野结衣视频在线播放 | 欧美极度另类极品 | 白丝美女私视频 | 看一级特黄a大一片 | 特一级黄 | 亚洲成精品动漫久久精久 | 黄色一级网 |