熬夜整理的c/c++萬字總結(jié)(一),值得收藏晒旅!

這一定是你需要的電子書資源,全谈秫!點(diǎn)擊查看!

程序員書籍資源硕淑,點(diǎn)擊查看置媳!

一. C語言概述

歡迎大家來到c語言的世界,c語言是一種強(qiáng)大的專業(yè)化的編程語言寥袭。

1.1 C語言的起源

貝爾實(shí)驗(yàn)室的Dennis Ritchie在1972年開發(fā)了C,當(dāng)時(shí)他正與ken Thompson一起設(shè)計(jì)UNIX操作系統(tǒng)杰扫,然而涉波,C并不是完全由Ritchie構(gòu)想出來的苍日。它來自Thompson的B語言辜纲。

1.2 使用C語言的理由

在過去的幾十年中,c語言已成為最流行和最重要的編程語言之一扫俺。它之所以得到發(fā)展狼纬,是因?yàn)槿藗儑L試使用它后都喜歡它疗琉。過去很多年中,許多人從c語言轉(zhuǎn)而使用更強(qiáng)大的c++語言柠贤,但c有其自身的優(yōu)勢种吸,仍然是一種重要的語言镜盯,而且它還是學(xué)習(xí)c++的必經(jīng)之路速缆。

高效性艺糜。c語言是一種高效的語言。c表現(xiàn)出通常只有匯編語言才具有的精細(xì)的控制能力(匯編語言是特定cpu設(shè)計(jì)所采用的一組內(nèi)部制定的助記符真慢。不同的cpu類型使用不同的匯編語言)。如果愿意朗鸠,您可以細(xì)調(diào)程序以獲得最大的速度或最大的內(nèi)存使用率烛占。

可移植性。c語言是一種可移植的語言弦赖。意味著蹬竖,在一個(gè)系統(tǒng)上編寫的c程序經(jīng)過很少改動(dòng)或不經(jīng)過修改就可以在其他的系統(tǒng)上運(yùn)行。

強(qiáng)大的功能和靈活性。c強(qiáng)大而又靈活阴绢。比如強(qiáng)大靈活的UNIX操作系統(tǒng)便是用c編寫的呻袭。其他的語言(Perl左电、Python段誊、BASIC枕扫、Pascal)的許多編譯器和解釋器也都是用c編寫的诗鸭。結(jié)果是當(dāng)你在一臺(tái)Unix機(jī)器上使用Python時(shí)锻弓,最終由一個(gè)c程序負(fù)責(zé)生成最后的可執(zhí)行程序青灼。

1.3 C語言標(biāo)準(zhǔn)

1.3.1 K&R C

起初,C語言沒有官方標(biāo)準(zhǔn)。1978年由美國電話電報(bào)公司(AT&T)貝爾實(shí)驗(yàn)室正式發(fā)表了C語言筋粗。布萊恩?柯林漢(Brian Kernighan) 和 丹尼斯?里奇(Dennis Ritchie) 出版了一本書,名叫《The C Programming Language》沛婴。這本書被 C語言開發(fā)者們稱為K&R宫仗,很多年來被當(dāng)作 C語言的非正式的標(biāo)準(zhǔn)說明。人們稱這個(gè)版本的 C語言為K&R C毅贮。

K&R C主要介紹了以下特色:結(jié)構(gòu)體(struct)類型;長整數(shù)(long int)類型瑰煎;無符號整數(shù)(unsigned int)類型;把運(yùn)算符=+和=-改為+=和-=赋铝。因?yàn)?+和=-會(huì)使得編譯器不知道使用者要處理i = -10還是i =- 10农尖,使得處理上產(chǎn)生混淆。

即使在后來ANSI C標(biāo)準(zhǔn)被提出的許多年后窟扑,K&R C仍然是許多編譯器的最準(zhǔn)要求,許多老舊的編譯器仍然運(yùn)行K&R C的標(biāo)準(zhǔn)。

1.3.2 ANSI C/C89標(biāo)準(zhǔn)

1970到80年代洗显,C語言被廣泛應(yīng)用处窥,從大型主機(jī)到小型微機(jī)滔驾,也衍生了C語言的很多不同版本。1983年俄讹,美國國家標(biāo)準(zhǔn)協(xié)會(huì)(ANSI)成立了一個(gè)委員會(huì)X3J11哆致,來制定 C語言標(biāo)準(zhǔn)。

1989年患膛,美國國家標(biāo)準(zhǔn)協(xié)會(huì)(ANSI)通過了C語言標(biāo)準(zhǔn)摊阀,被稱為ANSI X3.159-1989 "Programming Language C"。因?yàn)檫@個(gè)標(biāo)準(zhǔn)是1989年通過的踪蹬,所以一般簡稱C89標(biāo)準(zhǔn)枝缔。有些人也簡稱ANSI C,因?yàn)檫@個(gè)標(biāo)準(zhǔn)是美國國家標(biāo)準(zhǔn)協(xié)會(huì)(ANSI)發(fā)布的酝豪。

1990年揉阎,國際標(biāo)準(zhǔn)化組織(ISO)和國際電工委員會(huì)(IEC)把C89標(biāo)準(zhǔn)定為C語言的國際標(biāo)準(zhǔn)垮衷,命名為ISO/IEC 9899:1990 - Programming languages -- C[5]? 徐许。因?yàn)榇藰?biāo)準(zhǔn)是在1990年發(fā)布的,所以有些人把簡稱作C90標(biāo)準(zhǔn)吨枉。不過大多數(shù)人依然稱之為C89標(biāo)準(zhǔn),因?yàn)榇藰?biāo)準(zhǔn)與ANSI C89標(biāo)準(zhǔn)完全等同堪滨。

1994年了讨,國際標(biāo)準(zhǔn)化組織(ISO)和國際電工委員會(huì)(IEC)發(fā)布了C89標(biāo)準(zhǔn)修訂版伶棒,名叫ISO/IEC 9899:1990/Cor 1:1994[6]? 畴蹭,有些人簡稱為C94標(biāo)準(zhǔn)提澎。

1995年,國際標(biāo)準(zhǔn)化組織(ISO)和國際電工委員會(huì)(IEC)再次發(fā)布了C89標(biāo)準(zhǔn)修訂版寺庄,名叫ISO/IEC 9899:1990/Amd 1:1995 - C Integrity[7]? 话速,有些人簡稱為C95標(biāo)準(zhǔn)。

1.3.3 C99標(biāo)準(zhǔn)

1999年1月错维,國際標(biāo)準(zhǔn)化組織(ISO)和國際電工委員會(huì)(IEC)發(fā)布了C語言的新標(biāo)準(zhǔn)僧界,名叫ISO/IEC 9899:1999 - Programming languages -- C 举反,簡稱C99標(biāo)準(zhǔn)牧挣。這是C語言的第二個(gè)官方標(biāo)準(zhǔn)。

例如:

增加了新關(guān)鍵字 restrict醒陆,inline瀑构,_Complex,_Imaginary刨摩,_Bool

支持 long long寺晌,long double _Complex,float _Complex 這樣的類型

支持了不定長的數(shù)組澡刹。數(shù)組的長度就可以用變量了呻征。聲明類型的時(shí)候呢,就用 int a[*] 這樣的寫法。不過考慮到效率和實(shí)現(xiàn)罢浇,這玩意并不是一個(gè)新類型陆赋。

二、內(nèi)存分區(qū)

2.1 數(shù)據(jù)類型

2.1.1 數(shù)據(jù)類型概念

什么是數(shù)據(jù)類型嚷闭?為什么需要數(shù)據(jù)類型?

數(shù)據(jù)類型是為了更好進(jìn)行內(nèi)存的管理,讓編譯器能確定分配多少內(nèi)存攒岛。

我們現(xiàn)實(shí)生活中,狗是狗胞锰,鳥是鳥等等灾锯,每一種事物都有自己的類型,那么程序中使用數(shù)據(jù)類型也是來源于生活胜蛉。

當(dāng)我們給狗分配內(nèi)存的時(shí)候挠进,也就相當(dāng)于給狗建造狗窩,給鳥分配內(nèi)存的時(shí)候誊册,也就是給鳥建造一個(gè)鳥窩领突,我們可以給他們各自建造一個(gè)別墅,但是會(huì)造成內(nèi)存的浪費(fèi)案怯,不能很好的利用內(nèi)存空間君旦。

我們在想,如果給鳥分配內(nèi)存嘲碱,只需要鳥窩大小的空間就夠了金砍,如果給狗分配內(nèi)存,那么也只需要狗窩大小的內(nèi)存麦锯,而不是給鳥和狗都分配一座別墅恕稠,造成內(nèi)存的浪費(fèi)。

當(dāng)我們定義一個(gè)變量扶欣,a = 10,編譯器如何分配內(nèi)存鹅巍?計(jì)算機(jī)只是一個(gè)機(jī)器千扶,它怎么知道用多少內(nèi)存可以放得下10?

所以說骆捧,數(shù)據(jù)類型非常重要澎羞,它可以告訴編譯器分配多少內(nèi)存可以放得下我們的數(shù)據(jù)。

狗窩里面是狗敛苇,鳥窩里面是鳥妆绞,如果沒有數(shù)據(jù)類型,你怎么知道冰箱里放得是一頭大象!

數(shù)據(jù)類型基本概念:

類型是對數(shù)據(jù)的抽象;

類型相同的數(shù)據(jù)具有相同的表示形式枫攀、存儲(chǔ)格式以及相關(guān)操作;

程序中所有的數(shù)據(jù)都必定屬于某種數(shù)據(jù)類型;

數(shù)據(jù)類型可以理解為創(chuàng)建變量的模具: 固定大小內(nèi)存的別名;

2.1.2 數(shù)據(jù)類型別名

typedef?unsigned?int?u32;

typedef?struct?_PERSON{

char?name[64];

int?age;

}Person;

voidtest(){

u32?val;?//相當(dāng)于?unsigned?int?val;

Person?person;?//相當(dāng)于?struct?PERSON?person;

}

2.1.3 void數(shù)據(jù)類型

void字面意思是”無類型”,void* 無類型指針括饶,無類型指針可以指向任何類型的數(shù)據(jù)。

void定義變量是沒有任何意義的脓豪,當(dāng)你定義void a巷帝,編譯器會(huì)報(bào)錯(cuò)。

void真正用在以下兩個(gè)方面:

對函數(shù)返回的限定扫夜;

對函數(shù)參數(shù)的限定楞泼;

//1.?void修飾函數(shù)參數(shù)和函數(shù)返回

void?test01(void){

printf("hello?world");

}

//2.?不能定義void類型變量

voidtest02(){

void?val;?//報(bào)錯(cuò)

}

//3.?void*?可以指向任何類型的數(shù)據(jù),被稱為萬能指針

voidtest03(){

int?a?=?10;

void*?p?=?NULL;

p?=?&a;

printf("a:%d\n",*(int*)p);

char?c?='a';

p?=?&c;

printf("c:%c\n",*(char*)p);

}

//4.?void*?常用于數(shù)據(jù)類型的封裝

voidtest04(){

//void?*?memcpy(void?*?_Dst,?const?void?*?_Src,?size_t?_Size);

}

2.1.4 sizeof 操作符

sizeof 是 c語言中的一個(gè)操作符笤闯,類似于++堕阔、--等等。sizeof 能夠告訴我們編譯器為某一特定數(shù)據(jù)或者某一個(gè)類型的數(shù)據(jù)在內(nèi)存中分配空間時(shí)分配的大小颗味,大小以字節(jié)為單位超陆。

基本語法:

sizeof(變量);

sizeof?變量;

sizeof(類型);

sizeof 注意點(diǎn)

sizeof返回的占用空間大小是為這個(gè)變量開辟的大小浦马,而不只是它用到的空間时呀。和現(xiàn)今住房的建筑面積和實(shí)用面積的概念差不多。所以對結(jié)構(gòu)體用的時(shí)候晶默,大多情況下就得考慮字節(jié)對齊的問題了谨娜;

sizeof返回的數(shù)據(jù)結(jié)果類型是unsigned int;

要注意數(shù)組名和指針變量的區(qū)別磺陡。通常情況下趴梢,我們總覺得數(shù)組名和指針變量差不多,但是在用sizeof的時(shí)候差別很大币他,對數(shù)組名用sizeof返回的是整個(gè)數(shù)組的大小坞靶,而對指針變量進(jìn)行操作的時(shí)候返回的則是指針變量本身所占得空間,在32位機(jī)的條件下一般都是4蝴悉。而且當(dāng)數(shù)組名作為函數(shù)參數(shù)時(shí)彰阴,在函數(shù)內(nèi)部,形參也就是個(gè)指針拍冠,所以不再返回?cái)?shù)組的大心蛘狻廉丽;

//1.?sizeof基本用法

voidtest01(){

int?a?=?10;

printf("len:%d\n",?sizeof(a));

printf("len:%d\n",?sizeof(int));

printf("len:%d\n",?sizeof?a);

}

//2.?sizeof?結(jié)果類型

voidtest02(){

unsigned?int?a?=?10;

if(a?-?11?<?0){

printf("結(jié)果小于0\n");

}

else{

printf("結(jié)果大于0\n");

}

int?b?=?5;

if(sizeof(b)?-?10?<?0){

printf("結(jié)果小于0\n");

}

else{

printf("結(jié)果大于0\n");

}

}

//3.?sizeof?碰到數(shù)組

void?TestArray(int?arr[]){

printf("TestArray?arr?size:%d\n",sizeof(arr));

}

voidtest03(){

int?arr[]?=?{?10,?20,?30,?40,?50?};

printf("array?size:?%d\n",sizeof(arr));

//數(shù)組名在某些情況下等價(jià)于指針

int*?pArr?=?arr;

printf("arr[2]:%d\n",pArr[2]);

printf("array?size:?%d\n",?sizeof(pArr));

//數(shù)組做函數(shù)函數(shù)參數(shù),將退化為指針,在函數(shù)內(nèi)部不再返回?cái)?shù)組大小

TestArray(arr);

}

2.1.5 數(shù)據(jù)類型總結(jié)

數(shù)據(jù)類型本質(zhì)是固定內(nèi)存大小的別名妻味,是個(gè)模具,C語言規(guī)定:通過數(shù)據(jù)類型定義變量欣福;

數(shù)據(jù)類型大小計(jì)算(sizeof)责球;

可以給已存在的數(shù)據(jù)類型起別名typedef;

數(shù)據(jù)類型的封裝(void 萬能類型)拓劝;

2.2 變量

2.1.1 變量的概念

既能讀又能寫的內(nèi)存對象雏逾,稱為變量;

若一旦初始化后不能修改的對象則稱為常量郑临。

變量定義形式:?類型??標(biāo)識(shí)符,?標(biāo)識(shí)符,?…?,?標(biāo)識(shí)符

2.1.2 變量名的本質(zhì)

變量名的本質(zhì):一段連續(xù)內(nèi)存空間的別名栖博;

程序通過變量來申請和命名內(nèi)存空間 int a = 0;

通過變量名訪問內(nèi)存空間厢洞;

不是向變量名讀寫數(shù)據(jù)仇让,而是向變量所代表的內(nèi)存空間中讀寫數(shù)據(jù);

修改變量的兩種方式:

voidtest(){

int?a?=?10;

//1.?直接修改

a?=?20;

printf("直接修改,a:%d\n",a);

//2.?間接修改

int*?p?=?&a;

*p?=?30;

printf("間接修改,a:%d\n",?a);

}

2.3 程序的內(nèi)存分區(qū)模型

2.3.1 內(nèi)存分區(qū)

2.3.1.1 運(yùn)行之前

我們要想執(zhí)行我們編寫的c程序躺翻,那么第一步需要對這個(gè)程序進(jìn)行編譯丧叽。

1)預(yù)處理:宏定義展開、頭文件展開公你、條件編譯踊淳,這里并不會(huì)檢查語法

2)編譯:檢查語法,將預(yù)處理后文件編譯生成匯編文件

3)匯編:將匯編文件生成目標(biāo)文件(二進(jìn)制文件)

4)鏈接:將目標(biāo)文件鏈接為可執(zhí)行程序

? 代碼區(qū)

存放 CPU 執(zhí)行的機(jī)器指令陕靠。通常代碼區(qū)是可共享的(即另外的執(zhí)行程序可以調(diào)用它)迂尝,使其可共享的目的是對于頻繁被執(zhí)行的程序,只需要在內(nèi)存中有一份代碼即可剪芥。代碼區(qū)通常是只讀的垄开,使其只讀的原因是防止程序意外地修改了它的指t令。另外粗俱,代碼區(qū)還規(guī)劃了局部變量的相關(guān)信息说榆。

? 全局初始化數(shù)據(jù)區(qū)/靜態(tài)數(shù)據(jù)區(qū)(data段)

該區(qū)包含了在程序中明確被初始化的全局變量、已經(jīng)初始化的靜態(tài)變量(包括全局靜態(tài)變量和t)和常量數(shù)據(jù)(如字符串常量)寸认。

? 未初始化數(shù)據(jù)區(qū)(又叫 bss 區(qū))

存入的是全局未初始化變量和未初始化靜態(tài)變量签财。未初始化數(shù)據(jù)區(qū)的數(shù)據(jù)在程序開始執(zhí)行之前被內(nèi)核初始化為 0 或者空(NULL)。

總體來講說偏塞,程序源代碼被編譯之后主要分成兩種段:程序指令(代碼區(qū))和程序數(shù)據(jù)(數(shù)據(jù)區(qū))唱蒸。代碼段屬于程序指令,而數(shù)據(jù)域段和.bss段屬于程序數(shù)據(jù)灸叼。

那為什么把程序的指令和程序數(shù)據(jù)分開呢神汹?

程序被load到內(nèi)存中之后庆捺,可以將數(shù)據(jù)和代碼分別映射到兩個(gè)內(nèi)存區(qū)域。由于數(shù)據(jù)區(qū)域?qū)M(jìn)程來說是可讀可寫的屁魏,而指令區(qū)域?qū)Τ绦騺碇v說是只讀的滔以,所以分區(qū)之后呢,可以將程序指令區(qū)域和數(shù)據(jù)區(qū)域分別設(shè)置成可讀可寫或只讀氓拼。這樣可以防止程序的指令有意或者無意被修改你画;

當(dāng)系統(tǒng)中運(yùn)行著多個(gè)同樣的程序的時(shí)候,這些程序執(zhí)行的指令都是一樣的桃漾,所以只需要內(nèi)存中保存一份程序的指令就可以了坏匪,只是每一個(gè)程序運(yùn)行中數(shù)據(jù)不一樣而已,這樣可以節(jié)省大量的內(nèi)存撬统。比如說之前的Windows Internet Explorer 7.0運(yùn)行起來之后适滓, 它需要占用112 844KB的內(nèi)存,它的私有部分?jǐn)?shù)據(jù)有大概15 944KB恋追,也就是說有96 900KB空間是共享的凭迹,如果程序中運(yùn)行了幾百個(gè)這樣的進(jìn)程,可以想象共享的方法可以節(jié)省大量的內(nèi)存苦囱。

2.3.1.1 運(yùn)行之后

程序在加載到內(nèi)存前蕊苗,代碼區(qū)和全局區(qū)(data和bss)的大小就是固定的,程序運(yùn)行期間不能改變沿彭。然后朽砰,運(yùn)行可執(zhí)行程序,操作系統(tǒng)把物理硬盤程序load(加載)到內(nèi)存喉刘,除了根據(jù)可執(zhí)行程序的信息分出代碼區(qū)(text)瞧柔、數(shù)據(jù)區(qū)(data)和未初始化數(shù)據(jù)區(qū)(bss)之外,還額外增加了棧區(qū)睦裳、堆區(qū)造锅。

? 代碼區(qū)(text segment)

加載的是可執(zhí)行文件代碼段,所有的可執(zhí)行代碼都加載到代碼區(qū)廉邑,這塊內(nèi)存是不可以在運(yùn)行期間修改的哥蔚。

? 未初始化數(shù)據(jù)區(qū)(BSS)

加載的是可執(zhí)行文件BSS段,位置可以分開亦可以緊靠數(shù)據(jù)段蛛蒙,存儲(chǔ)于數(shù)據(jù)段的數(shù)據(jù)(全局未初始化糙箍,靜態(tài)未初始化數(shù)據(jù))的生存周期為整個(gè)程序運(yùn)行過程。

? 全局初始化數(shù)據(jù)區(qū)/靜態(tài)數(shù)據(jù)區(qū)(data segment)

加載的是可執(zhí)行文件數(shù)據(jù)段牵祟,存儲(chǔ)于數(shù)據(jù)段(全局初始化深夯,靜態(tài)初始化數(shù)據(jù),文字常量(只讀))的數(shù)據(jù)的生存周期為整個(gè)程序運(yùn)行過程。

? 棧區(qū)(stack)

棧是一種先進(jìn)后出的內(nèi)存結(jié)構(gòu)咕晋,由編譯器自動(dòng)分配釋放雹拄,存放函數(shù)的參數(shù)值、返回值掌呜、局部變量等滓玖。在程序運(yùn)行過程中實(shí)時(shí)加載和釋放,因此质蕉,局部變量的生存周期為申請到釋放該段椖刈玻空間。

? 堆區(qū)(heap)

堆是一個(gè)大容器饰剥,它的容量要遠(yuǎn)遠(yuǎn)大于棧,但沒有棧那樣先進(jìn)后出的順序摧阅。用于動(dòng)態(tài)內(nèi)存分配汰蓉。堆在內(nèi)存中位于BSS區(qū)和棧區(qū)之間。一般由程序員分配和釋放棒卷,若程序員不釋放顾孽,程序結(jié)束時(shí)由操作系統(tǒng)回收。

2.3.2 分區(qū)模型

2.3.2.1 棧區(qū)

由系統(tǒng)進(jìn)行內(nèi)存的管理比规。主要存放函數(shù)的參數(shù)以及局部變量若厚。在函數(shù)完成執(zhí)行,系統(tǒng)自行釋放棧區(qū)內(nèi)存蜒什,不需要用戶管理测秸。

#char*?func(){

char?p[]?="hello?world!";?//在棧區(qū)存儲(chǔ)?亂碼

printf("%s\n",?p);

returnp;

}

voidtest(){

char*?p?=?NULL;

p?=?func();

printf("%s\n",p);

}

2.3.2.2 堆區(qū)

由編程人員手動(dòng)申請,手動(dòng)釋放灾常,若不手動(dòng)釋放霎冯,程序結(jié)束后由系統(tǒng)回收,生命周期是整個(gè)程序運(yùn)行期間钞瀑。使用malloc或者new進(jìn)行堆的申請沈撞。

char*func(){

char*?str?=?malloc(100);

strcpy(str,"hello?world!");

printf("%s\n",str);

returnstr;

}

voidtest01(){

char*?p?=?NULL;

p?=?func();

printf("%s\n",p);

}

void?allocateSpace(char*?p){

p?=?malloc(100);

strcpy(p,"hello?world!");

printf("%s\n",?p);

}

voidtest02(){

char*?p?=?NULL;

allocateSpace(p);

printf("%s\n",?p);

}

堆分配內(nèi)存API:

#include?<stdlib.h>

void?*calloc(size_t?nmemb,?size_t?size);

功能:

在內(nèi)存動(dòng)態(tài)存儲(chǔ)區(qū)中分配nmemb塊長度為size字節(jié)的連續(xù)區(qū)域。calloc自動(dòng)將分配的內(nèi)存 置0雕什。

參數(shù):

nmemb:所需內(nèi)存單元數(shù)量

size:每個(gè)內(nèi)存單元的大胁场(單位:字節(jié))

返回值:

成功:分配空間的起始地址

失敗:NULL

#include?<stdlib.h>

void?*realloc(void?*ptr,?size_t?size);

功能:

重新分配用malloc或者calloc函數(shù)在堆中分配內(nèi)存空間的大小贷岸。

realloc不會(huì)自動(dòng)清理增加的內(nèi)存壹士,需要手動(dòng)清理,如果指定的地址后面有連續(xù)的空間偿警,那么就會(huì)在已有地址基礎(chǔ)上增加內(nèi)存墓卦,如果指定的地址后面沒有空間,那么realloc會(huì)重新分配新的連續(xù)內(nèi)存户敬,把舊內(nèi)存的值拷貝到新內(nèi)存落剪,同時(shí)釋放舊內(nèi)存睁本。

參數(shù):

ptr:為之前用malloc或者calloc分配的內(nèi)存地址,如果此參數(shù)等于NULL忠怖,那么和realloc與malloc功能一致

size:為重新分配內(nèi)存的大小, 單位:字節(jié)

返回值:

成功:新分配的堆內(nèi)存地址

失斈匮摺:NULL

voidtest01(){

int*?p1?=?calloc(10,sizeof(int));

if(p1?==?NULL){

return;

}

for(int?i?=?0;?i?<?10;?i?++){

p1[i]?=?i?+?1;

}

for(int?i?=?0;?i?<?10;?i++){

printf("%d?",p1[i]);

}

printf("\n");

free(p1);

}

voidtest02(){

int*?p1?=?calloc(10,?sizeof(int));

if(p1?==?NULL){

return;

}

for(int?i?=?0;?i?<?10;?i++){

p1[i]?=?i?+?1;

}

int*?p2?=?realloc(p1,?15?*?sizeof(int));

if(p2?==?NULL){

return;

}

printf("%d\n",?p1);

printf("%d\n",?p2);

//打印

for(int?i?=?0;?i?<?15;?i++){

printf("%d?",?p2[i]);

}

printf("\n");

//重新賦值

for(int?i?=?0;?i?<?15;?i++){

p2[i]?=?i?+?1;

}

//再次打印

for(int?i?=?0;?i?<?15;?i++){

printf("%d?",?p2[i]);

}

printf("\n");

free(p2);

}

2.3.2.3 全局/靜態(tài)區(qū)

全局靜態(tài)區(qū)內(nèi)的變量在編譯階段已經(jīng)分配好內(nèi)存空間并初始化。這塊內(nèi)存在程序運(yùn)行期間一直存在,它主要存儲(chǔ)全局變量凡泣、靜態(tài)變量和常量枉疼。

注意:

(1)這里不區(qū)分初始化和未初始化的數(shù)據(jù)區(qū),是因?yàn)殪o態(tài)存儲(chǔ)區(qū)內(nèi)的變量若不顯示初始化鞋拟,則編譯器會(huì)自動(dòng)以默認(rèn)的方式進(jìn)行初始化骂维,即靜態(tài)存儲(chǔ)區(qū)內(nèi)不存在未初始化的變量。

(2)全局靜態(tài)存儲(chǔ)區(qū)內(nèi)的常量分為常變量和字符串常量贺纲,一經(jīng)初始化航闺,不可修改。靜態(tài)存儲(chǔ)內(nèi)的常變量是全局變量猴誊,與局部常變量不同潦刃,區(qū)別在于局部常變量存放于棧,實(shí)際可間接通過指針或者引用進(jìn)行修改懈叹,而全局常變量存放于靜態(tài)常量區(qū)則不可以間接修改乖杠。

(3)字符串常量存儲(chǔ)在全局/靜態(tài)存儲(chǔ)區(qū)的常量區(qū)。

int?v1?=?10;//全局/靜態(tài)區(qū)

const?int?v2?=?20;?//常量澄成,一旦初始化胧洒,不可修改

static?int?v3?=?20;?//全局/靜態(tài)區(qū)

char?*p1;?//全局/靜態(tài)區(qū),編譯器默認(rèn)初始化為NULL

//那么全局static?int?和?全局int變量有什么區(qū)別墨状?

voidtest(){

static?int?v4?=?20;?//全局/靜態(tài)區(qū)

}

char*func(){

static?char?arr[]?="hello?world!";?//在靜態(tài)區(qū)存儲(chǔ)?可讀可寫

arr[2]?='c';

char*?p?="hello?world!";?//全局/靜態(tài)區(qū)-字符串常量區(qū)

//p[2]?='c';?//只讀略荡,不可修改

printf("%d\n",arr);

printf("%d\n",p);

printf("%s\n",?arr);

returnarr;

}

voidtest(){

char*?p?=?func();

printf("%s\n",p);

}

2.3.2.4 總結(jié)

在理解C/C++內(nèi)存分區(qū)時(shí),常會(huì)碰到如下術(shù)語:數(shù)據(jù)區(qū)歉胶,堆汛兜,棧,靜態(tài)區(qū)通今,常量區(qū)粥谬,全局區(qū),字符串常量區(qū)辫塌,文字常量區(qū)漏策,代碼區(qū)等等,初學(xué)者被搞得云里霧里臼氨。在這里掺喻,嘗試捋清楚以上分區(qū)的關(guān)系。

數(shù)據(jù)區(qū)包括:堆,棧感耙,全局/靜態(tài)存儲(chǔ)區(qū)褂乍。

全局/靜態(tài)存儲(chǔ)區(qū)包括:常量區(qū),全局區(qū)即硼、靜態(tài)區(qū)逃片。

常量區(qū)包括:字符串常量區(qū)、常變量區(qū)只酥。

代碼區(qū):存放程序編譯后的二進(jìn)制代碼褥实,不可尋址區(qū)。

可以說裂允,C/C++內(nèi)存分區(qū)其實(shí)只有兩個(gè)损离,即代碼區(qū)和數(shù)據(jù)區(qū)。

2.3.3 函數(shù)調(diào)用模型

2.3.3.1 函數(shù)調(diào)用流程

棧(stack)是現(xiàn)代計(jì)算機(jī)程序里最為重要的概念之一绝编,幾乎每一個(gè)程序都使用了棧僻澎,沒有棧就沒有函數(shù),沒有局部變量瓮增,也就沒有我們?nèi)缃衲芤姷降乃杏?jì)算機(jī)的語言。在解釋為什么棧如此重要之前哩俭,我們先了解一下傳統(tǒng)的棧的定義:

在經(jīng)典的計(jì)算機(jī)科學(xué)中绷跑,棧被定義為一個(gè)特殊的容器,用戶可以將數(shù)據(jù)壓入棧中(入棧凡资,push),也可以將壓入棧中的數(shù)據(jù)彈出(出棧隙赁,pop),但是棧容器必須遵循一條規(guī)則:先入棧的數(shù)據(jù)最后出棧(First In Last Out,FILO).

在經(jīng)典的操作系統(tǒng)中,椀嗫ィ總是向下增長的厚掷。壓棧的操作使得棧頂?shù)牡刂窚p小,彈出操作使得棧頂?shù)刂吩龃蟆?/p>

棧在程序運(yùn)行中具有極其重要的地位冒黑。最重要的,棧保存一個(gè)函數(shù)調(diào)用所需要維護(hù)的信息掩驱,這通常被稱為堆棧幀(Stack Frame)或者活動(dòng)記錄(Activate Record).一個(gè)函數(shù)調(diào)用過程所需要的信息一般包括以下幾個(gè)方面:

函數(shù)的返回地址;

函數(shù)的參數(shù)欧穴;

臨時(shí)變量民逼;

保存的上下文:包括在函數(shù)調(diào)用前后需要保持不變的寄存器。

我們從下面的代碼苔可,分析以下函數(shù)的調(diào)用過程:

int?func(int?a,int?b){

int?t_a?=?a;

int?t_b?=?b;

returnt_a?+?t_b;

}

intmain(){

int?ret?=?0;

ret?=?func(10,?20);

returnEXIT_SUCCESS;

}

2.3.3.2 調(diào)用慣例

現(xiàn)在缴挖,我們大致了解了函數(shù)調(diào)用的過程,這期間有一個(gè)現(xiàn)象焚辅,那就是函數(shù)的調(diào)用者和被調(diào)用者對函數(shù)調(diào)用有著一致的理解映屋,例如,它們雙方都一致的認(rèn)為函數(shù)的參數(shù)是按照某個(gè)固定的方式壓入棧中同蜻。如果不這樣的話棚点,函數(shù)將無法正確運(yùn)行。

如果函數(shù)調(diào)用方在傳遞參數(shù)的時(shí)候先壓入a參數(shù)湾蔓,再壓入b參數(shù)瘫析,而被調(diào)用函數(shù)則認(rèn)為先壓入的是b,后壓入的是a,那么被調(diào)用函數(shù)在使用a,b值時(shí)候,就會(huì)顛倒默责。

因此贬循,函數(shù)的調(diào)用方和被調(diào)用方對于函數(shù)是如何調(diào)用的必須有一個(gè)明確的約定,只有雙方都遵循同樣的約定桃序,函數(shù)才能夠被正確的調(diào)用杖虾,這樣的約定被稱為”調(diào)用慣例(Calling Convention)”.一個(gè)調(diào)用慣例一般包含以下幾個(gè)方面:

函數(shù)參數(shù)的傳遞順序和方式

函數(shù)的傳遞有很多種方式,最常見的是通過棧傳遞媒熊。函數(shù)的調(diào)用方將參數(shù)壓入棧中奇适,函數(shù)自己再從棧中將參數(shù)取出。對于有多個(gè)參數(shù)的函數(shù)芦鳍,調(diào)用慣例要規(guī)定函數(shù)調(diào)用方將參數(shù)壓棧的順序:從左向右嚷往,還是從右向左。有些調(diào)用慣例還允許使用寄存器傳遞參數(shù)柠衅,以提高性能皮仁。

棧的維護(hù)方式

在函數(shù)將參數(shù)壓入棧中之后,函數(shù)體會(huì)被調(diào)用菲宴,此后需要將被壓入棧中的參數(shù)全部彈出,以使得棧在函數(shù)調(diào)用前后保持一致付燥。這個(gè)彈出的工作可以由函數(shù)的調(diào)用方來完成键科,也可以由函數(shù)本身來完成勋颖。

為了在鏈接的時(shí)候?qū)φ{(diào)用慣例進(jìn)行區(qū)分饭玲,調(diào)用慣例要對函數(shù)本身的名字進(jìn)行修飾。不同的調(diào)用慣例有不同的名字修飾策略矮冬。

事實(shí)上,在c語言里窑滞,存在著多個(gè)調(diào)用慣例哀卫,而默認(rèn)的是cdecl.任何一個(gè)沒有顯示指定調(diào)用慣例的函數(shù)都是默認(rèn)是cdecl慣例此改。比如我們上面對于func函數(shù)的聲明带斑,它的完整寫法應(yīng)該是:

int?_cdecl?func(int?a,int?b);

注意:cdecl不是標(biāo)準(zhǔn)的關(guān)鍵字勋磕,在不同的編譯器里可能有不同的寫法挂滓,例如gcc里就不存在_cdecl這樣的關(guān)鍵字赶站,而是使用__attribute_((cdecl)).

2.3.3.2 函數(shù)變量傳遞分析

2.3.4 棧的生長方向和內(nèi)存存放方向

//1.?棧的生長方向

voidtest01(){

int?a?=?10;

int?b?=?20;

int?c?=?30;

int?d?=?40;

printf("a?=?%d\n",?&a);

printf("b?=?%d\n",?&b);

printf("c?=?%d\n",?&c);

printf("d?=?%d\n",?&d);

//a的地址大于b的地址烙博,故而生長方向向下

}

//2.?內(nèi)存生長方向(小端模式)

voidtest02(){

//高位字節(jié)?->?地位字節(jié)

int?num?=?0xaabbccdd;

unsigned?char*?p?=?#

//從首地址開始的第一個(gè)字節(jié)

printf("%x\n",*p);

printf("%x\n",?*(p?+?1));

printf("%x\n",?*(p?+?2));

printf("%x\n",?*(p?+?3));

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末铺根,一起剝皮案震驚了整個(gè)濱河市乔宿,隨后出現(xiàn)的幾起案子掂林,更是在濱河造成了極大的恐慌党饮,老刑警劉巖刑顺,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蹲堂,死亡現(xiàn)場離奇詭異,居然都是意外死亡播聪,警方通過查閱死者的電腦和手機(jī)离陶,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進(jìn)店門霎俩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來打却,“玉大人柳击,你說我怎么就攤上這事片习。” “怎么了具垫?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長起宽。 經(jīng)常有香客問我坯沪,道長腐晾,這世上最難降的妖魔是什么藻糖? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮洋满,結(jié)果婚禮上牺勾,老公的妹妹穿的比我還像新娘禽最。我一直安慰自己,他們只是感情好虑乖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布仅叫。 她就那樣靜靜地躺著诫咱,像睡著了一般坎缭。 火紅的嫁衣襯著肌膚如雪掏呼。 梳的紋絲不亂的頭發(fā)上铅檩,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天拾给,我揣著相機(jī)與錄音鸣戴,去河邊找鬼。 笑死入偷,一個(gè)胖子當(dāng)著我的面吹牛疏之,可吹牛的內(nèi)容都是我干的锋爪。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼钧忽!你這毒婦竟也來了毯炮?” 一聲冷哼從身側(cè)響起逼肯,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎桃煎,沒想到半個(gè)月后篮幢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡为迈,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年洲拇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片曲尸。...
    茶點(diǎn)故事閱讀 39,795評論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出骆姐,到底是詐尸還是另有隱情带射,我是刑警寧澤灿里,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布甸祭,位于F島的核電站校焦,受9級特大地震影響报亩,放射性物質(zhì)發(fā)生泄漏劲件。R本人自食惡果不足惜烤宙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一叉瘩、第九天 我趴在偏房一處隱蔽的房頂上張望汤徽。 院中可真熱鬧泰鸡,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铣焊。三九已至邑狸,卻和暖如春犁苏,著一層夾襖步出監(jiān)牢的瞬間袁勺,已是汗流浹背期丰。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工端辱, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留喷鸽,地道東北人轩性。 一個(gè)月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓茫经,卻偏偏與公主長得像巷波,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子卸伞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容