C語(yǔ)言高級(jí)部分總結(jié)
C語(yǔ)言高級(jí)部分?
?
一、內(nèi)存大話題
1.0、內(nèi)存就是程序的立足之地,體現(xiàn)內(nèi)存重要性。
1.1、內(nèi)存理解:內(nèi)存物理看是有很多個(gè)Bank(就是行列陣式的存儲(chǔ)芯片),每一個(gè)Bank的列就是位寬 ,每一行就是Words,則存儲(chǔ)單元數(shù)量=行數(shù)(words)×列數(shù)(位寬)×Bank的數(shù)量;通常也用M×W的方式來(lái)表示芯片的容量(或者說(shuō)是芯片的規(guī)格/組織結(jié)構(gòu))。
? ? ? ? ? M是以位寬為單位的總?cè)萘?,單位是?,W代表位寬, 單位是bit。計(jì)算出來(lái)的芯片容量也是以bit為單位,但用戶可以采用除以8的方法換算為字節(jié)(Byte)。比如8M×8,這是一個(gè)8bit位寬芯片,有8M個(gè)存儲(chǔ)單元,總?cè)萘渴?4Mbit(8MB)。?
1.2、c語(yǔ)言中其實(shí)沒(méi)有bool類型:以0表示假,非0表示真,則在內(nèi)存存儲(chǔ)是以int型存放的。如果想要表示真假,可以用int/char型做替換,在c++中就有bool x=true/false;? ?
1.3、內(nèi)存對(duì)齊:內(nèi)存對(duì)齊(提高訪問(wèn)效率速度,編譯器一般默認(rèn)是4字節(jié)對(duì)齊)
1.4、char/int/short/long/float/double型:放在內(nèi)存的長(zhǎng)度和解析作用。(int *)0,使0地址指向一個(gè)int型。又比如0000111010101可以解析成int型也可以解析成float型。? ?
1.5、Linux內(nèi)核是面向?qū)ο蟮模鴆語(yǔ)言是面向過(guò)程的,但可以用結(jié)構(gòu)體內(nèi)嵌指針變成面向?qū)ο蟆H?
struct student
{
int age; ? ?//變量
int lenth; ? //將相當(dāng)于一個(gè)類,有變量有函數(shù)
char *name;
void (*eat)(void); ?//函數(shù)指針
?}
1.6、棧的理解:
(1) 運(yùn)行時(shí)自動(dòng)分配&自動(dòng)回收:棧是自動(dòng)管理的,程序員不需要手工干預(yù)。方便簡(jiǎn)單。(表現(xiàn)在匯編代碼,編譯時(shí),會(huì)自動(dòng)編譯成匯編碼實(shí)現(xiàn)函數(shù)調(diào)用完立即改變棧頂)
(2) 反復(fù)使用:棧內(nèi)存在程序中其實(shí)就是那一塊空間,程序反復(fù)使用這一塊空間。(硬件上有個(gè)寄存器,用來(lái)存放棧的棧頂?shù)刂?,棧是有大小的空間)
(3) 臟內(nèi)存:棧內(nèi)存由于反復(fù)使用,每次使用后程序不會(huì)去清理,因此分配到時(shí)保留原來(lái)的值。
(4) 臨時(shí)性:(函數(shù)不能返回棧變量的指針,因?yàn)檫@個(gè)空間是臨時(shí)的)
(5) 棧會(huì)溢出:因?yàn)椴僮飨到y(tǒng)事先給定了棧的大小,如果在函數(shù)中無(wú)窮盡的分配棧內(nèi)存總能用完。棧的操作(怎么出棧怎么入棧)是由具體硬件來(lái)干預(yù),程序員只要明白原理就可以了, 但是要給相應(yīng)的棧寄存器賦值。當(dāng)調(diào)用函數(shù)時(shí),變量會(huì)自動(dòng)放在棧中(入棧)當(dāng)函數(shù)調(diào)用完后,棧會(huì)自動(dòng)出棧.
? ( 6 ) ?棧的 "發(fā)展"有四種情況,滿增棧,滿減棧,空增棧,空減棧,至于是那種要根據(jù)編譯器決定,而s5pv210 是滿減棧。
1.7、堆的理解:
(1)操作系統(tǒng)堆管理器管理:堆管理器是操作系統(tǒng)的一個(gè)模塊,堆管理內(nèi)存分配靈活,按需分配。
(2)大塊內(nèi)存:堆內(nèi)存管理者總量很大的操作系統(tǒng)內(nèi)存塊,各進(jìn)程可以按需申請(qǐng)使用,使用完釋放。 ? ??
(3)臟內(nèi)存:堆內(nèi)存也是反復(fù)使用的,而且使用者用完釋放前不會(huì)清除,因此也是臟的。
(4)臨時(shí)性:堆內(nèi)存只在malloc和free之間屬于我這個(gè)進(jìn)程,而可以訪問(wèn)。在malloc之前和free之后都不能再訪問(wèn),否則會(huì)有不可預(yù)料的后果。
(5)程序手動(dòng)申請(qǐng)&釋放:手工意思是需要寫代碼去申請(qǐng)malloc和釋放free。(記住:不要把申請(qǐng)的地址給搞丟了, 不然自己用不了,也釋放不了)
? ? ? ? ? 申請(qǐng)一段內(nèi)存,可以是: malloc(10*sizeof ( int ) ); 原型:void *malloc(size_t size); ?//指針函數(shù) size_t是宏定義int 都是便于可移植性 ,返回一個(gè)內(nèi)存地址,void *可以看出,希望申請(qǐng)的內(nèi)存用來(lái)存放什么就強(qiáng)制類型什么。? ? ?calloc( 10,sizeof ( int ) ); 原型:void *calloc(size_t nmemb, size_t size);// nmemb個(gè)單元,每個(gè)單元size字節(jié)
? ? ? ? ? ? void *realloc(void *ptr, size_t size);// 改變?cè)瓉?lái)申請(qǐng)的空間的大小的ptr是原來(lái)申請(qǐng)內(nèi)存的指針,size是想要重新申請(qǐng)內(nèi)存的大小
使用就是*(p+1)=12 ? ; ? ?*(P+3)=110;
? ? ? 申請(qǐng)失敗返回NULL,申請(qǐng)成功返回一個(gè)地址,申請(qǐng)之后一定要檢驗(yàn)(NULL!=p)用完一定要 ? free ( p ) ?;釋放后不是不能用,是不應(yīng)該使用了??梢越o它“洗盤子‘,p=NULL;? 其實(shí)申請(qǐng)的內(nèi)存并不能真正改變大小,原理是先重新申請(qǐng)一段內(nèi)存,然后把原來(lái)申請(qǐng)的內(nèi)存上的內(nèi)容復(fù)制到新的內(nèi)存上,然后釋放掉原來(lái)的內(nèi)存,返回新的指針。
? ?(6) 在申請(qǐng)內(nèi)存時(shí),malloc(0)其實(shí)也是成功的,因?yàn)橄到y(tǒng)規(guī)定少于一定數(shù)目的大小,都申請(qǐng)規(guī)定的大小,如在win32系統(tǒng)下申請(qǐng)少于32字節(jié)的地址,最后申請(qǐng)到的空間是32字節(jié)。
1.8、內(nèi)存里的數(shù)據(jù):?
(1)代碼段:存放代碼二進(jìn)制、常量(char *p="linux",則”linux“存放在代碼段,是不可更改的)
(2) 數(shù)據(jù)段: ?存放非0全局變量、靜態(tài)局部變量(局部只屬于函數(shù)的,不是整個(gè)程序的)
(3) bss ? ? : ?存放為0的全局變量/為0的靜態(tài)局部變量、存放未初始化全局變量/靜態(tài)局部變量
? 注意:const int a=9; 有兩種存放方式:第一種確實(shí)存放在代碼段,讓a不能修改,第二種是仍然存放在數(shù)據(jù)段中,讓編譯器來(lái)判斷,如果有改變的代碼就會(huì)報(bào)錯(cuò)。 至于那種,是不確定的,像單片機(jī)就屬于第一種。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
1.9、《1》一個(gè)源文件實(shí)際上是以段為單位編譯成連接成可執(zhí)行文件(a .out );這個(gè)可執(zhí)行文件總的說(shuō)是分為數(shù)據(jù)段,代碼段,自定義段,數(shù)據(jù)段還可以細(xì)分成 .bbs 段。而雜段會(huì)在執(zhí)行的時(shí)候拿掉。所以a.out分為雜段,數(shù)據(jù)段(存放的是非0全局變量).bbs段,代碼段。
《2》內(nèi)存實(shí)際上被劃分了兩大區(qū)域,一個(gè)是系統(tǒng)區(qū)域,另一個(gè)是用戶區(qū)域,而每一個(gè)區(qū)域又被劃分成了幾個(gè)小區(qū)域,有堆,棧,代碼區(qū),.bbs區(qū),數(shù)據(jù)區(qū)(存放的是非0全局變量)。
? ? ? ? ?《3》對(duì)于有操作系統(tǒng)而言, ?當(dāng)我們?cè)趫?zhí)行a.out可執(zhí)行文件時(shí),執(zhí)行這個(gè)文件的那套程序會(huì)幫我們把雜段清掉,然后把相應(yīng)的段加載到內(nèi)存對(duì)應(yīng)的段。對(duì)于裸機(jī)程序而言,我們是使用一套工具將a.elf的可執(zhí)行程序給清掉了所有段的符號(hào)信息,把?純凈的二進(jìn)制做成.bin格式的燒錄文件。所以我們加載到內(nèi)存的程序是連續(xù)的,也就是說(shuō)代碼段和數(shù)據(jù)段、.bbs段都是連續(xù)的。當(dāng)然,??臻g是我們自己設(shè)置的。而且在裸機(jī)中我們不能使用malloc函數(shù),因?yàn)槲覀兪褂玫闹皇蔷幾g器、連接器工具沒(méi)有集成庫(kù)函數(shù),沒(méi)有定義堆空間區(qū)。
《4》大總結(jié)多程序運(yùn)行情況: 在Linux系統(tǒng)中運(yùn)行cdw1.out時(shí),運(yùn)行這個(gè)文件的那套程序會(huì)幫我們把相應(yīng)的段加載到內(nèi)存對(duì)應(yīng)的段。然后操作系統(tǒng)會(huì)把下載到內(nèi)存的具體物理地址與每條命令(32位)的鏈接地址映射到TTB中(一段內(nèi)存空間),當(dāng)我們又運(yùn)行cdw2.out時(shí),同樣也像cdw1.out一樣加載進(jìn)去,并映射到TTB表中。而且這兩個(gè).out文件默認(rèn)都是鏈接0地址(邏輯),當(dāng)cpu發(fā)出一個(gè)虛擬地址(Linux中程序邏輯地址)通過(guò)TTB查找的物理地址是不一樣的。所以對(duì)于每一個(gè)程序而言,它獨(dú)占4G的內(nèi)存空間,看不到其他程序。
二、位操作
2.1 ?~(0u)是全1;
2.2 位與& ? ? 位或 | ? ? ? ? ? ?位取反~ ? ? ? 位異或^
2.3、位與、位或、位異或的特點(diǎn)總結(jié):
? ? ? ? ?位與:(任何數(shù),其實(shí)就是1或者0)與1位與無(wú)變化,與0位與變成0
? ? ? ? ?位或:(任何數(shù),其實(shí)就是1或者0)與1位或變成1,與0位或無(wú)變化
? ? ? ? ?位異或:(任何數(shù),其實(shí)就是1或者0)與1位異或會(huì)取反,與0位異或無(wú)變化
?
2.4、左移位<< 與右移位>> ?C語(yǔ)言的移位要取決于數(shù)據(jù)類型。
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 對(duì)于無(wú)符號(hào)數(shù),左移時(shí)右側(cè)補(bǔ)0(相當(dāng)于邏輯移位)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 對(duì)于無(wú)符號(hào)數(shù),右移時(shí)左側(cè)補(bǔ)0(相當(dāng)于邏輯移位)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 對(duì)于有符號(hào)數(shù),左移時(shí)右側(cè)補(bǔ)0(叫算術(shù)移位,相當(dāng)于邏輯移位)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 對(duì)于有符號(hào)數(shù),右移時(shí)左側(cè)補(bǔ)符號(hào)位(如果正數(shù)就補(bǔ)0,負(fù)數(shù)就補(bǔ)1,叫算術(shù)移位)
?
2.5、小記:常與 ?1 拿來(lái) 做位運(yùn)算。讓他取反、移位 得到想要的數(shù)。
2.6、直接用宏來(lái)置位、復(fù)位(最右邊為第1位)。 置位置1,復(fù)位置0 ?;
? ? ? ? ? #define SET_NTH_BIT(x, n) ?(x | ((1U)<<(n-1)))
? ? ? ? ? #define CLEAR_NTH_BIT(x, n) (x & ~((1U)<<(n-1)))
三、指針—精髓?
3.1 ?printf("%p n"); 其中%p表示輸出一個(gè)指針,就是指針變量(其存放的那個(gè)地址),可以理解為輸出一個(gè)地址。
3.2 ?int* p1, p2 ; ? ? 等同于 int *p1; ? int ?p2; int *p="Linux",其不能改變*P,因?yàn)椤保欤椋睿酰⑹且粋€(gè)常數(shù)。
3.3 ( 代碼規(guī)范性 )在定義指針時(shí),同時(shí)賦值為NULL,在用指針時(shí),先判斷它是不是NULL。尤其是在malloc申請(qǐng)內(nèi)存后,free(p);則一定要讓p=NULL
3.4 ?C/C++中對(duì)NULL的理解: #ifdef _cplusplus// 定義這個(gè)符號(hào)就表示當(dāng)前是C++環(huán)境
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?#define NULL 0;// 在C++中NULL就是0
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?#else
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?#define NULL (void *) 0;// 在C中NULL是強(qiáng)制類型轉(zhuǎn)換為void *的0
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?#endif
3.5、修飾詞:const (修飾變量為常量,應(yīng)該理解為不應(yīng)該去變它,當(dāng)作常量,而并非永遠(yuǎn)不能改變,當(dāng)然要看具體運(yùn)行環(huán)境,在gcc,const 這種就可以采用指針?lè)绞叫薷?,但?br />在VC6.6++中就不可以修改):其雖然是當(dāng)作常數(shù),但是仍然存放在數(shù)據(jù)段中,用指針仍然可以改變值。
第一種:const int *p; ??
第二種:int const *p;
第三種:int * const p;
第四種:const int * const p;
3.6、 數(shù)組 int a[2]; 其中a是指首元素的首地址,&a是整個(gè)數(shù)組的收地址(數(shù)組指針,其這個(gè)指針指向一個(gè)數(shù)組),他們的值是一樣的,但意義不一樣,可以參照 int a; int *p=&a; 來(lái)理解。數(shù)組和指針天生姻緣在于數(shù)組名;int a[3]; int* p=a;是可以的,但是 int *p=&a;就會(huì)報(bào)錯(cuò),盡管他們的值是一樣的,但意義不一樣,所以是不允許的,除非強(qiáng)制類型轉(zhuǎn)換。在訪問(wèn)時(shí)是a[0],其實(shí)編譯器會(huì)把它變成*(a+0)的方式,只是用a[0]看起來(lái)更方便,封裝了一下而已,實(shí)質(zhì)還是指針。
3.7、 siziof()是一個(gè)運(yùn)算符,測(cè)試所占內(nèi)存空間,如 int a[100] ;sizeof(a)=400;?與strlen( )要有所區(qū)別,他是測(cè)字符串實(shí)際長(zhǎng)度的,不包括‘