總結(jié))
一.指針簡(jiǎn)介及定義C語言中指針是一種數(shù)據(jù)結(jié)構(gòu)其定義為存儲(chǔ)地址值的數(shù)據(jù)它的存在為訪問數(shù)據(jù)提供了一種新的方式使用指針按照規(guī)律取用數(shù)據(jù)變得更加快捷高效也讓數(shù)組、結(jié)構(gòu)體等大型數(shù)據(jù)能夠作為函數(shù)參數(shù)使得靈活分配內(nèi)存成為可能即使是一個(gè)函數(shù)也可以作為參數(shù)輸入另一個(gè)函數(shù)。也就是說一個(gè)指針變量可以指向每一種數(shù)據(jù)類型每一種數(shù)據(jù)都可以通過指針來訪問和傳遞并且通過地址值的傳遞能夠做到直接修改。指針變量指的是一類變量他們有相同的功能和特點(diǎn)包括普通類型指針、指向行向量的指針、指針數(shù)組、空指針、函數(shù)指針。二.指針的運(yùn)算2.1取地址通過地址符號(hào)可以將普通數(shù)據(jù)類型的地址取出并賦值給指針量.2.2解運(yùn)算通過*p可以獲得指針p所指向變量的值亦可以對(duì)其進(jìn)行賦值則將改變p指針?biāo)缸兞勘旧淼闹怠?.3對(duì)指針變量進(jìn)行加減對(duì)指針變量進(jìn)行加減時(shí)務(wù)必要注意指針的初值最重要的是注意指針變量對(duì)應(yīng)的單位地址長(zhǎng)度例如整形指針加一表示其實(shí)際地址值加4int類型大小為4個(gè)字節(jié)。尤其是定義了數(shù)組指針時(shí)要注意該數(shù)組的大小。我們知道數(shù)組的名是一個(gè)指針常量它可以參與運(yùn)算但是不能改變他本身的值。指向同一數(shù)組的不同元素地址的指針可以進(jìn)行減法運(yùn)算不能進(jìn)行加法運(yùn)算。2.4指針變量進(jìn)行比較指向同一數(shù)組的不同元素地址的指針可以進(jìn)行比較我們知道在定義一個(gè)變量或者數(shù)組亦或者結(jié)構(gòu)體時(shí)他們的地址是系統(tǒng)隨機(jī)分配的并不連續(xù)可能差距極大所以對(duì)不同變量的指針是不允許進(jìn)行比較的。三.對(duì)不同指針變量的詳細(xì)介紹3.1指向基本數(shù)據(jù)類型的指針。int a,*pa NULL(表示指針不指向任何數(shù)NULL 0唯一可以賦值給指針的整數(shù));float b,*pb NULL;char c,*pc NULL;pa a;pb b;pc c;也可以將數(shù)組中的某個(gè)元素的地址值賦給指針變量通過取地址并賦值的形式可以將一個(gè)普通數(shù)據(jù)類型的地址值直接給到指針進(jìn)行賦值。從而可以用該指針對(duì)所指的變量進(jìn)行取用于是當(dāng)一個(gè)普通變量作為參數(shù)傳入函數(shù)中時(shí)可以將形參定義為普通類型也可以定義為指針的形式區(qū)別在于指針傳遞可以改變?cè)兞康闹礽nt array[3] {0};(外部數(shù)組和自動(dòng)數(shù)組忘記賦初值默認(rèn)為0函數(shù)內(nèi)部定義的數(shù)組忘記賦初值則會(huì)隨機(jī)賦值)float f[5] {0};char string[] Hello world;pa array;pb float;pc string;(字符類型的指針還可以通過直接將字符串賦值的方式進(jìn)行賦值實(shí)際上是把該字符串的第一個(gè)字符的地址給到了pc)printf(%s,pc;pa、pb、pc都滿足以下等式通過連續(xù)取地址加解地址的方式取用數(shù)組中的值效率更高、運(yùn)算速度更快。*pa f[0] pa[0];*(pa1) f[1] pa[1];……3.2指向行向量的指針變量定義方式為以整形行向量為例,浮點(diǎn)數(shù)類型和字符類型行向量均相同int*pa[4];int array [3][4] {0};pa array;(可以將一個(gè)二維數(shù)組的數(shù)組名賦給它)pa array1;pa array2;以上三個(gè)式子表示的pa分別指向array的每一行的第一個(gè)元素但他的物理含義不是一個(gè)基本指針變量那樣他的單位長(zhǎng)度是一個(gè)4個(gè)整形變量的行向量需要通過解運(yùn)算才可以將他的轉(zhuǎn)化成一個(gè)指向該行第一個(gè)變量的指針。3.3數(shù)組指針數(shù)組指針顧名思義該數(shù)組的每一個(gè)變量都是一個(gè)指針且都是同一類型的指針。定義方式如下int*p[4];float*pf[5];char*pc[6](可用于處理字符串?dāng)?shù)組);3.4指向指針的指針跟套娃一樣就不多贅述了。3.5空指針void *p;表示指針p是一個(gè)通用類型的指針其他類型的指針量可以直接賦值給他而將他賦值給其他類型指針時(shí)必須進(jìn)行強(qiáng)制類型轉(zhuǎn)換可用于通用函數(shù)的撰寫。3.6指向函數(shù)的指針該指針儲(chǔ)存的實(shí)際上是一個(gè)函數(shù)被記錄在內(nèi)存區(qū)的首地址通過指向數(shù)組的指針可以將函數(shù)作為參數(shù)傳入另一個(gè)函數(shù)中,從而可以利用不同函數(shù)間的組合靈活的實(shí)現(xiàn)一些功能。double func1(int a,int b);(一個(gè)函數(shù)的聲明)double (*p)(int a,int b);p func1;于是p(1,6) func1(1,6) (*p)(1,6);四.指針用于動(dòng)態(tài)分配內(nèi)存空間和創(chuàng)建動(dòng)態(tài)數(shù)組。動(dòng)態(tài)一維數(shù)組創(chuàng)建#includestdlib.h#define n 10int *p malloc(n*sizeof(int));使用p[0]、p[1]或者int*q p;再使用q的自增使用數(shù)組元素free(p);記得釋放內(nèi)存否則這段內(nèi)存將一直被占用。動(dòng)態(tài)二維數(shù)組創(chuàng)建;int**p (int**)malloc(row*sizeof(int *));for(int i 0;irow;i)//list是二維數(shù)組的列數(shù){p[i] (int*)malloc(list*sizeof(int));}釋放內(nèi)存for(int i 0;irow;i)//list是二維數(shù)組的列數(shù){free(p[i]);}free(p);四.C數(shù)組與指針的聯(lián)系與區(qū)別1.關(guān)聯(lián)數(shù)組的名本身是個(gè)指針常量他不占據(jù)內(nèi)存空間。所有變量名在編譯前作為字符兩存儲(chǔ)在硬盤中形成一章映射著所儲(chǔ)存對(duì)象的初始地址、內(nèi)存占用大小、類型等信息的表然后在編譯過程中會(huì)查表來進(jìn)行相應(yīng)操作編譯完成后這個(gè)表就被銷毀了所以不會(huì)占用內(nèi)存。對(duì)于一維數(shù)組來說數(shù)組名的值為數(shù)組的第一個(gè)元素的地址值所以一維數(shù)組的取用方式包括兩種int array[10] {0};//定義一個(gè)一維整型數(shù)組int *p array;可以通過元素序號(hào)和將數(shù)組名作為一個(gè)指針常量計(jì)算進(jìn)行遍歷for(int i 0;i10;i){printf(%d\n,array[i]);//方式一printf(%d\n,*(ai));//方式二}for(;parray[9];){printf(%d\n,*p);//方式三}二維數(shù)組要稍顯復(fù)雜一些二維數(shù)組的名同樣是一個(gè)指針常量但是他的所屬指針類型是指向數(shù)組的指針?biāo)膯挝婚L(zhǎng)度是該二維數(shù)組一行的大小對(duì)于二維數(shù)組同樣有兩種取用的方式int array[4][3] {0};//定義一個(gè)二維數(shù)組。int (*p)[3] array;//定義了一個(gè)數(shù)組指針指向行向量的指針可以將數(shù)組的名給到這個(gè)指針。int *q array[0];//或者q array[0][0];都是將數(shù)組的第一個(gè)元素地址給到一個(gè)整型指for(int i0;i4;i)for(int j 0;j3;j){printf(%d%c,a[i][j],(j2)?\n: );//方式一printf(%d%c,*(*(ai)j),(j2)?\n: );//方式二}for(;qarray[4][3];q){printf(%d%c,q,(q4-array[0][0])%160?\n: );//方式三}不論是一維數(shù)組還是二維數(shù)組這兩種方式本質(zhì)上都是通過指針的方式進(jìn)行的取值例如a[5]來計(jì)算機(jī)內(nèi)就是*(a5)先計(jì)算元素的地址值然后取用由于數(shù)組的名本身就是一個(gè)指針常量所以可以將數(shù)組名賦值給一個(gè)同類型的指針一旦進(jìn)行了賦值該指針就指向了該數(shù)組的第一個(gè)元素。然后就可以利用指針來進(jìn)行對(duì)數(shù)組的調(diào)用。如以上代碼中的方式三所示。方式三通過指針的規(guī)律性的增加來取用數(shù)組元素效率更高。同樣由于數(shù)組名本身是一個(gè)指針常量所以可以將數(shù)組名給到一個(gè)函數(shù)的指針形參從而將數(shù)組首地址傳過去再利用指針進(jìn)行取用即使函數(shù)的形參就是一個(gè)數(shù)組并后面跟上了該數(shù)組的大小也是將數(shù)組名給到函數(shù)從而進(jìn)行地址值的傳遞不過在函數(shù)中我們往往將他按照數(shù)組元素索引的方式進(jìn)行取用。2.區(qū)別與數(shù)組名類型相同的指針變量可以獲取數(shù)組名的值但他們?cè)谟行┯梅ㄉ洗嬖诓町愑捎谠贑語言中數(shù)組總是在定義時(shí)就完成了內(nèi)存地址的分配所以數(shù)組名的值即數(shù)組第一個(gè)元素的地址值是固定不變的后續(xù)也不能發(fā)生改變這也就意味著對(duì)于一個(gè)數(shù)組不可以進(jìn)行整個(gè)數(shù)組的賦值只能遍歷元素來改變其值例如char string[] Hello World;string good morning;//出錯(cuò)一方面后一個(gè)數(shù)組比string的內(nèi)存要大其次string是不可以發(fā)生改變的左值即不可以把右邊這個(gè)字符串的首地址值給到string,從而完成數(shù)組的傳遞。但是對(duì)于一個(gè)指針變量來說就完全不是問題可以通過多次賦值使得指針指向不同的數(shù)組。總結(jié)對(duì)于一個(gè)數(shù)組來說分別對(duì)他的數(shù)組名、首元素取地址以及他數(shù)組名本身給到一個(gè)指針得到的地址值都是一樣的但所蘊(yùn)含的信息不同。五.數(shù)組指針與二級(jí)指針1.數(shù)組指針可以通過動(dòng)態(tài)內(nèi)存分配完成一個(gè)二維數(shù)組的建立而二級(jí)指針可以形成一個(gè)高級(jí)的數(shù)組其中元素為一個(gè)數(shù)組。而二維數(shù)組在傳遞參數(shù)以及獲取地址的時(shí)候會(huì)自動(dòng)降維成數(shù)組指針。例如想用一個(gè)數(shù)組指針構(gòu)造一個(gè)二維數(shù)組int(*p)[10] (int(*)[10])malloc(sizeof(int[10])*maxsize);而用二維數(shù)組構(gòu)建一個(gè)動(dòng)態(tài)的高級(jí)數(shù)組就比較麻煩了;int**arr (int**)malloc(sizeof(int*)*maxsize);此后還需要每次對(duì)arr這個(gè)指針數(shù)組中的每個(gè)指針進(jìn)行動(dòng)態(tài)分配或者賦值arr[0] (int*)malloc(sizeof(int)*maxsize);(以此類推)2.函數(shù)的指針傳遞其實(shí)質(zhì)仍然是地址傳遞例如;void func(int**nums,int numsSize;int* numsColumnSize);六.函數(shù)傳遞參數(shù)與指針當(dāng)你希望一個(gè)指針在函數(shù)內(nèi)部改變其指向的時(shí)候應(yīng)該傳遞一個(gè)他的二級(jí)指針進(jìn)去。七.對(duì)于C語言中數(shù)據(jù)的存儲(chǔ)在使用指針和數(shù)組的情況下的思考。1.聲明變量可以獲得自動(dòng)分配的棧內(nèi)存用來存儲(chǔ)數(shù)據(jù)但會(huì)在作用域結(jié)束后被自動(dòng)銷毀。2.有了變量可以將變量的地址給到相同類型的指針于是就有兩種可以取用數(shù)據(jù)的方式一中是變量一種是指針解引用二者都可以沒有好壞之分。3.因?yàn)閿?shù)組變量在聲明時(shí)就必須給出一個(gè)用常量聲明的固定的大小而不能說用變量來聲明其大小所以有了手動(dòng)管理內(nèi)存分配的動(dòng)態(tài)分配而動(dòng)態(tài)分配的結(jié)果是分配一段內(nèi)存并將內(nèi)存的頭地址給到某個(gè)指針這意味著這段動(dòng)態(tài)內(nèi)存就只能使用指針來訪問了且不會(huì)自動(dòng)銷毀必須手動(dòng)釋放。4.指針是一種非常靈活的數(shù)據(jù)大類其下除了最基本的內(nèi)置類型的指針還有多級(jí)指針、數(shù)組指針、函數(shù)指針而且可以將指針也組合成數(shù)組的形式一維數(shù)組指針、二維數(shù)組指針如此嵌套就有很多很多不同的存儲(chǔ)形式十分的靈活。而我們要使用這些組織形式各異類型各異的指針最重要的就是搞清楚我究竟想存儲(chǔ)的是什么類型的數(shù)據(jù)這些數(shù)據(jù)單獨(dú)來看需要哪些指針最后綜合成一個(gè)整個(gè)的指針映射框架。5.C語言中函數(shù)是不能接受大量的數(shù)據(jù)以及返回大量的數(shù)據(jù)的所以指針用來傳遞地址從而可以將一段連續(xù)的數(shù)據(jù)或者一些可以由指針訪問的數(shù)據(jù)傳入函數(shù)中從而進(jìn)行訪問和修改const變量可以禁止修改并且也只有動(dòng)態(tài)分配的內(nèi)存指針可以將局部作用域產(chǎn)生的大量數(shù)據(jù)傳遞出來此外我們可以看出在C語言中想要將一些數(shù)據(jù)真正的拷貝而不是值傳遞就需要對(duì)他們進(jìn)行取地址。注程序在編譯成機(jī)器語言并執(zhí)行之前只是一段文本而已其中的變量名也好指針名也好所有的代號(hào)都只是為了人類閱讀方便在真正編譯的時(shí)候會(huì)將這些名稱按照內(nèi)部的映射表進(jìn)行轉(zhuǎn)換為對(duì)應(yīng)的信息然后參與運(yùn)算所以變量名在程序運(yùn)行時(shí)是不需要存儲(chǔ)空間的。一個(gè)對(duì)象的名稱在編譯時(shí)會(huì)映射成一張表而一個(gè)名稱對(duì)應(yīng)的關(guān)系不僅僅是起始地址還包括他的大小類型這些信息都會(huì)在編譯時(shí)用到用完即銷毀。而利用變量名進(jìn)行參數(shù)傳遞或者賦予指針值時(shí)確實(shí)是將變量名本身攜帶的信息中的首地址給到了指針但如果僅看一個(gè)變量或者一個(gè)數(shù)組名絕對(duì)不是一個(gè)常量指針這些變量名在定義的時(shí)候就與一個(gè)地址綁定與一個(gè)類型綁定甚至與一塊內(nèi)存的大小綁定了與這個(gè)數(shù)據(jù)的信息綁定。數(shù)組名會(huì)在需要的時(shí)候進(jìn)行退化C語言設(shè)計(jì)哲學(xué)按理來說應(yīng)該一個(gè)一維數(shù)組的名應(yīng)該給到的是數(shù)組指針但實(shí)際并不是而是降維成了一個(gè)普通指針因?yàn)橛?jì)算機(jī)底層始終是那幾種基礎(chǔ)內(nèi)置實(shí)體在互相計(jì)算其余的數(shù)據(jù)類型不過是組織形式邏輯契約罷了為了方便取用這些實(shí)體數(shù)據(jù)數(shù)組名會(huì)自動(dòng)進(jìn)行一次降維但是在獲取其信息例如SIZEOF的時(shí)候仍然是取的數(shù)據(jù)名綁定的信息中的大小。這也是為什么我們?cè)趯⒍S數(shù)組名傳遞和劃分一塊連續(xù)內(nèi)存作為二維數(shù)組的動(dòng)態(tài)內(nèi)存時(shí)我們會(huì)定義一個(gè)數(shù)組指針而結(jié)構(gòu)體變量并不會(huì)自動(dòng)降維成指針是可以整個(gè)進(jìn)行拷貝值傳遞的但是通常不推薦int arr[2][3] {{}};int(*p)[3] arrint *p[10] (int(8)[10]) malloc (sizeof(int[10])*size);就已經(jīng)分配好了二維數(shù)組的內(nèi)存。以及為什么對(duì)于一維數(shù)組我們是如下操作int arr[10] {};int*p arr;int*p (int*)malloc(sizeof(int)*size);