核心內(nèi)容: 1、C語(yǔ)言指針的核心知識(shí)點(diǎn) 2、處理指針相關(guān)問(wèn)題的萬(wàn)能措施—-內(nèi)存分配圖 3、C語(yǔ)言的指針是如何過(guò)渡到java中的引用的
最近一段時(shí)間一直在學(xué)習(xí)C語(yǔ)言的指針,也算是頗有心得吧,雖然從網(wǎng)上看了幾篇關(guān)于指針的博文,但是感覺(jué)都不符合自己的口味,于是決定好好寫一篇關(guān)于指針的文章。 C語(yǔ)言指針的核心知識(shí)點(diǎn): 1、指針就是地址,地址就是內(nèi)存單元的編號(hào),范圍是0到4G-1,指針的本質(zhì)就是一個(gè)操作受限的非負(fù)整數(shù),而指針變量就是存放內(nèi)存單元編號(hào)(即地址、指針)的變量。 2、凡是動(dòng)態(tài)分配的內(nèi)存,都是沒(méi)有名字的,而是將其地址賦給一個(gè)指針變量,用指針變量去代表這個(gè)事物。 3、一個(gè)指針變量,無(wú)論其指向的變量占多少個(gè)字節(jié),其本身只占用4個(gè)字節(jié)的內(nèi)存空間,因?yàn)閮?nèi)存單元的編號(hào)是32位。32/8=4 4、字節(jié)是存儲(chǔ)數(shù)據(jù)的基本單元,一個(gè)字節(jié)占8位,而一個(gè)字節(jié)的編號(hào)占32位。 5、變量分為兩種類型:普通類型變量和指針類型變量,其中普通類型變量用來(lái)存放真實(shí)的數(shù)據(jù),而指針類型變量用來(lái)存放變量的地址。其中指針類型變量包括(Java中): ①所有類定義的變量:如 Student student = new Student(“zhang” , 25),其中的student ②所有接口定義的變量:如 List list = new ArrayList(),其中的list ③數(shù)組的名字:如int a[] = {1,2,3,8,9}中的a。 6、靜態(tài)內(nèi)存是在棧中進(jìn)行分配的,是由系統(tǒng)自動(dòng)分配、自動(dòng)釋放的,靜態(tài)內(nèi)存是程序員無(wú)法控制的;動(dòng)態(tài)內(nèi)存是在堆中進(jìn)行分配的,是由程序員手動(dòng)分配,手動(dòng)釋放的,凡是動(dòng)態(tài)分配的內(nèi)存必須通過(guò)free的方式才能夠進(jìn)行釋放,當(dāng)然這里指的是C語(yǔ)言;在Java當(dāng)中,動(dòng)態(tài)分配的內(nèi)存是由內(nèi)存回收機(jī)制來(lái)回收的,不用程序員來(lái)進(jìn)行手動(dòng)回收,保證了程序的安全性,但是在Java當(dāng)中,由于虛擬機(jī)要一直跟蹤每一塊內(nèi)存空間的使用情況,所以往往會(huì)從造成CPU的使用率過(guò)大。 好的,如果你想學(xué)會(huì)C語(yǔ)言中的指針,上面的這些內(nèi)容是你必須要理解的,首先我們先理解一下究竟什么是指針,在理解究竟什么是指針之前,我們必須要知道數(shù)據(jù)在內(nèi)存中究竟是如何來(lái)進(jìn)行存儲(chǔ)的,先放一張圖:
這里通過(guò)一個(gè)小例子來(lái)說(shuō)明數(shù)據(jù)在內(nèi)存中是如何來(lái)進(jìn)行存儲(chǔ)的:
當(dāng)我們?cè)赩isiual C++6.0軟件對(duì)這個(gè)程序進(jìn)行編譯運(yùn)行的時(shí)候,Visiual C++6.0這個(gè)軟件首先請(qǐng)求操作系統(tǒng)為我們的變量i分配一塊內(nèi)存空間,隨后操作系統(tǒng)會(huì)在內(nèi)存中尋找一塊空閑的區(qū)域分配給我們的程序,隨后Visiual C++6.0這個(gè)軟件會(huì)將變量i和我們的這塊內(nèi)存空間關(guān)聯(lián)起來(lái),今后對(duì)變量i的操作就是對(duì)我們內(nèi)存空間的操作。具體實(shí)現(xiàn)過(guò)程如下:
現(xiàn)在我們對(duì)內(nèi)存存儲(chǔ)的這一塊區(qū)域進(jìn)行放大:
操作系統(tǒng)會(huì)給我們的變量分配一塊內(nèi)存空間,但是這塊內(nèi)存空間究竟在內(nèi)存中的什么位置呢?這塊內(nèi)存空間在內(nèi)存空間中的編號(hào)到底是多少呢?現(xiàn)在讓我么在程序中輸出一下:
# include <stdio.h>int main(){ int i = 10; PRintf("編號(hào)1的數(shù)值是:%#X/n",&i); return 0;}1234567891012345678910運(yùn)行結(jié)果:
現(xiàn)在我們用圖畫在描述一下:
在上面這張圖中:
(即編號(hào)1)就是我們所說(shuō)的指針,即地址,也就是說(shuō):指針實(shí)際上就是內(nèi)存單元的編號(hào),一個(gè)編號(hào)為32位,每一個(gè)內(nèi)存單元都會(huì)占有一個(gè)內(nèi)部單元的編號(hào)(即地址)記載其在內(nèi)存條中的位置,因此通過(guò)指針我們可以直接對(duì)硬件進(jìn)行操作。 其實(shí),程序歸根結(jié)底就是對(duì)內(nèi)存的操作,我們對(duì)一塊內(nèi)存空間進(jìn)行操作總共含有兩種方式: ①直接通過(guò)變量名的方式對(duì)這塊內(nèi)存空間進(jìn)行操作。(直接訪問(wèn)) ②通過(guò)獲取內(nèi)存空間的地址對(duì)這塊內(nèi)存空間進(jìn)行操作。(間接訪問(wèn))
其中,第一種方式是我們經(jīng)常使用的,但是第二種方式會(huì)讓我們有一種直接接觸到硬件的感覺(jué),示例程序:
# include <stdio.h>int main(){ int i = 10; printf("編號(hào)1的數(shù)值是:%#X/n",&i); int * p = &i; //指針變量p保存了變量i的地址:18FF44 *p = 100; //以18FF44為地址的那塊內(nèi)存空間的內(nèi)容設(shè)置為100 printf("變量i的內(nèi)容是:%d/n",i);//無(wú)論是直接訪問(wèn)還是以地址的間接訪問(wèn),本質(zhì)上都是對(duì)同一塊內(nèi)存空間的訪問(wèn) return 0;}1234567891011121314151612345678910111213141516運(yùn)行結(jié)果:
具體效果:
歸根接地就是一句話:無(wú)論是通過(guò)變量名i的直接訪問(wèn),還是通過(guò)地址18FF44的間接訪問(wèn),本質(zhì)上都是對(duì)同一塊內(nèi)存空間的訪問(wèn)。
處理指針相關(guān)問(wèn)題的萬(wàn)能措施—-內(nèi)存分配圖 很多人在處理指針這塊的程序的時(shí)候,有的時(shí)候總是會(huì)感覺(jué)到很迷糊,但是就我個(gè)人而言,對(duì)于指針相關(guān)的知識(shí),總是習(xí)慣于去畫內(nèi)存分配圖去解決問(wèn)題,而且效果還是非常好的,下面我們就用一個(gè)典型的程序:交換內(nèi)容的程序來(lái)說(shuō)明問(wèn)題。 要求:輸入a和b兩個(gè)整數(shù),按照先大后小的順序輸出a和b。 實(shí)例程序1:
# include <stdio.h>void swap(int ,int );int main(){ int a,b; printf("請(qǐng)從鍵盤上輸入a和b兩個(gè)數(shù)值:/n"); scanf("%d %d",&a,&b); if (a < b) { swap(a,b); } printf("max=%d /t min=%d /n",a,b); return 0;}void swap(int p,int q){ int tmp; //交換p和q的內(nèi)容 tmp = p; p = q; q = tmp;}12345678910111213141516171819202122232425261234567891011121314151617181920212223242526運(yùn)行結(jié)果:
很明顯,從運(yùn)行結(jié)果上來(lái)看,并沒(méi)有達(dá)到我們的預(yù)期效果,下面我們用內(nèi)存分配圖來(lái)查找一下原因:
所以上面程序的解法是錯(cuò)誤的。 實(shí)例程序2:
# include <stdio.h>void swap(int *,int *);int main(){ int a,b; printf("請(qǐng)從鍵盤上輸入a和b兩個(gè)數(shù)值:/n"); scanf("%d %d",&a,&b); if (a < b) { swap(&a,&b); } printf("max=%d /t min=%d /n",a,b); return 0;}void swap(int *p,int *q){ int tmp; tmp = *p; *p = *q; *q = tmp;}12345678910111213141516171819202122232425261234567891011121314151617181920212223242526運(yùn)行結(jié)果:
內(nèi)存分配圖:
通過(guò)上面的圖解我們可以發(fā)現(xiàn),指針變量p和q分別定位到了變量a和變量b的內(nèi)存空間,間接的交換了a和b內(nèi)存空間的內(nèi)容。
C語(yǔ)言的指針是如何過(guò)渡到Java中的引用的 在談到這個(gè)問(wèn)題的時(shí)候,我認(rèn)為應(yīng)該從兩個(gè)方面進(jìn)行說(shuō)起:動(dòng)態(tài)內(nèi)存分配和如何傳遞發(fā)送內(nèi)容。 動(dòng)態(tài)內(nèi)存份分配的問(wèn)題: 實(shí)例程序1:
# include <stdio.h># include <malloc.h># include <string.h>struct Student{ char name[100]; int age; float score;};int main(){ Student * student = (Student *)malloc(sizeof(Student)); strcpy(student->name,"zhangming"); student->age = 25; student->score = 88.8f; printf("name is %s/n",student->name); //student->age在編譯底層會(huì)變?yōu)?*student).name printf("age is %d/n",student->age); printf("score is %f/n",student->score); return 0;}1234567891011121314151617181920212223242512345678910111213141516171819202122232425運(yùn)行結(jié)果:
內(nèi)存實(shí)例圖示:
對(duì)于上面的這個(gè)程序,Java語(yǔ)言是這么封裝的:
class Student { String name; int age; float score;}public class App1 { public static void main(String[] args) { //Student * student = (Student *)malloc(sizeof(Student)); Student student = new Student(); //new相當(dāng)于C語(yǔ)言中的malloc student.name = "zhangsan"; student.age = 25; student.score = 88.8f; System.out.println("name is:"+student.name); System.out.println("age is:"+student.age); System.out.println("score is:"+student.score); }}123456789101112131415161718192021123456789101112131415161718192021下面我們通過(guò)函數(shù)傳遞數(shù)據(jù):傳遞指針變量(本質(zhì)傳遞的是地址)
# include <stdio.h># include <malloc.h># include <string.h>struct Student{ char name[100]; int age; float score;};void changeData(Student * stu){ strcpy(stu->name,"lisi"); stu->age = 24; stu->score = 98.8f;}int main(){ Student * student = (Student *)malloc(sizeof(Student)); strcpy(student->name,"zhangming"); student->age = 25; student->score = 88.8f; printf("name is %s/n",student->name); //student->age在編譯底層會(huì)變?yōu)?*student).name printf("age is %d/n",student->age); printf("score is %f/n",student->score); changeData(student);//傳遞的是地址,速度快并且節(jié)省內(nèi)存空間! printf("name is %s/n",student->name); //student->age在編譯底層會(huì)變?yōu)?*student).name printf("age is %d/n",student->age); printf("score is %f/n",student->score); return 0;}123456789101112131415161718192021222324252627282930313233343536123456789101112131415161718192021222324252627282930313233343536運(yùn)行結(jié)果:
Java封裝的效果:
class Student { String name; int age; float score;}public class App1 { public static void main(String[] args) { //Student * student = (Student *)malloc(sizeof(Student)); Student student = new Student(); //new相當(dāng)于C語(yǔ)言中的malloc student.name = "zhangsan"; student.age = 25; student.score = 88.8f; System.out.println("name is:"+student.name); System.out.println("age is:"+student.age); System.out.println("score is:"+student.score); changeData(student); //student本質(zhì)上是一個(gè)指針變量 System.out.println("name is:"+student.name); System.out.println("age is:"+student.age); System.out.println("score is:"+student.score); } public static void changeData(Student stu) //stu指向同一塊內(nèi)存空間 { stu.name = "lisi"; stu.age = 24; stu.score = 98.8f; }}123456789101112131415161718192021222324252627282930313233123456789101112131415161718192021222324252627282930313233運(yùn)行結(jié)果:
name is:zhangsanage is:25score is:88.8name is:lisiage is:24score is:98.8123456123456總結(jié):在Java當(dāng)中,雖然已經(jīng)沒(méi)有了指針,但是底層編譯運(yùn)行過(guò)程中本質(zhì)上就是指針,Java中的引用本質(zhì)上就是C語(yǔ)言中的指針變量,無(wú)論是C語(yǔ)言還是Java語(yǔ)言,都有一個(gè)共同的特點(diǎn):凡是動(dòng)態(tài)分配的內(nèi)存都是沒(méi)有名字的,而是用一個(gè)指針變量保存這塊內(nèi)存空間的地址,用這個(gè)指針變量去代表這塊內(nèi)存空間。