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