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