C語言中文網 目錄
首頁 > 編程筆記 > C語言筆記 > 指針 閱讀:1,712

指針變量的存儲實質,C語言指針變量的存儲詳解

相信大家都知道這樣一個學習指針的觀點:要想徹底理解 C 語言中的指針,首先一定要理解 C 語言中變量的存儲實質。談到變量的存儲,我們就不得不先說說計算機的內存概念。計算機的內存是一個用于存儲數據的空間,由一系列連續的存儲單元組成,它就好像電影院中的座位一樣,如圖 1 所示。


圖 1 內存中某一區域的編號

在電影院中,為了保證大家能夠快速找到自己的座位,每個座位都用一個唯一的編號來標識。而對計算機內存來說,它同樣需要像座位一樣編號,這樣我們才能夠知道內存中的數據存放在什么位置,這就是我們所說的內存編址。如圖 1 所示,每一個內存單元都有一個唯一的地址,系統根據這個地址來識別內存單元,在地址所標識的存儲單元中存取數據。在這里,我們需要分清兩個概念:

內存單元的地址。如圖 1 中的編號(如 10000、10001 等),通過引用這些不同的地址編號,我們就可以使用不同內存單元中存儲的數據。值得注意的是,內存單元的地址是固定的,在程序中不能修改。


圖 2 內存單元中的數據

內存單元中的數據。如圖 2 中的表格內的數據,編號為 10000 的內存單元保存的數據為 119,編號為 10001 的內存單元保存的數據為 120,編號為 10002 的內存單元保存的數據為 121。與內存單元的地址不同,內存單元中的數據是可以被程序修改的,例如,可以在程序中將編號為 10001 的內存單元的值由 120 修改為 122。

在了解計算機內存之后,下面來看看 C 語言中的變量是如何存儲的,如下面的代碼所示:
int i;
char c;
在上面的代碼中,我們聲明了兩個變量,它們將要求系統在內存中分配一個類型為 int 型的存儲空間和一個類型為 char 型的存儲空間。因此,執行上面兩個語句后,內存中的映像可能如圖 3 所示。


圖 3 變量的存儲

如圖 3 所示,在 32 位計算機中,int 類型的變量占用 4 字節(即圖 3 中編號為 10000~10003,共 4 個存儲單元),char 類型的變量占用 1 字節(即圖中編號為 10004 的存儲單元)。其實這里很容易看出,變量名實質上就是內存單元地址的一個符號,如變量i代表內存地址 10000(變量所占內存單元的首地址),而變量 c 代表內存地址 10004。當用戶使用變量時,本質上是訪問該變量所對應的內存單元。

在申請變量之后,接下來需要為變量賦值,如下面的代碼所示:
i=100;
c='w';
對于上面的賦值語句,相信大家都能夠很好地理解,它表示將整型常量 100 保存到變量i中(實質上是將 100 保存到內存地址 10000 為起始地址的 4 個存儲單元中),而將字符常量 w 保存到變量 c 中(實質上是將 w 保存到內存地址為 10004 的存儲單元中)。因此,在執行上面的語句后,我們可以利用這樣的形象來理解,如圖 4 所示。


圖 4 變量賦值后的存儲

看到圖 4,你或許會問,為什么內存地址為 10004 的存儲單元存儲的是 119,而不是 w 呢?這很簡單,字符常量保存的是其 ASCII 碼值,所以在編號為 10004 的內存單元中保存的是字符常量 w 的 ASCII 碼 119。

到現在為止,相信你對變量的申請與賦值的內存分配變化都有了一定的了解。那么我們現在反過來繼續問,變量究竟存儲在哪里,我們要如何得到變量的存儲地址呢?

要想知道變量的存儲地址,就需要用到運算符“&”了,使用該運算符可獲得變量的內存單元地址(如果變量占用多個內存單元,將得到首地址)。例如,要在屏幕上顯示上面的變量 i 與 c 的地址值,可以使用如下代碼:
printf("%x\n", &i);
printf("%x\n", &c);
這里以上面圖中的內存映像為例,屏幕上顯示的不是 i 值 100,而是顯示i的內存地址編號 10000(即如果變量占用多個內存單元,將得到首地址)。同理,顯示的不是 c 值 w,而是顯示 c 的內存地址編號 10004。當然,在實際操作中,變量 i 與 w 的地址值不會是這個數了。

了解變量的存儲與地址之后,現在我們可以來看看指針的概念了。

在 C 語言中,將內存單元的編號或地址稱為指針。可通過一個變量來存放指針,這種變量稱為指針變量。因此,一個指針變量的值就是某個內存單元的地址,或稱為某內存單元的指針。它與其他一般變量所不同的是,一般的變量包含的是實際的、真實的數據;而指針只是一個指示器,它告訴程序在內存的哪塊區域可以找到數據。

下面來看一條聲明一個指向整型變量的指針的語句:
int *p;
很顯然,在上面的代碼中聲明了一個指針變量 p。在執行該的代碼后,我們可以通過圖 5 所示的存儲結構來理解(在 32 位系統中,指針的寬度是 4 字節)。


圖 5 指針變量 p 的存儲情況

由圖 5 中可以看出,指針變量 p 與前面的一般變量 i 與 c 并沒有什么本質的區別。那么,它為什么又會被稱為“指針”呢?其實,關鍵是要看這個變量所存儲的內容是什么。繼續來看下面的語句:
p = &i;
即上面的代碼表示把i地址的編號賦值給指針變量 p,也就是說在 p 里面寫上i內存地址編號 10000(如果變量占用多個內存單元,將得到首地址),結果圖 6 所示。


圖 6 執行 p=&i 后的存儲情況

如圖 6 所示,在指針變量 p 中保存變量i的首地址編號 10000,因此,通過指針變量 p 就可間接訪問內存單元 10000 開始的 4 個內存單元。也就是說,程序先通過指針變量 p 的值找到變量i的首地址編號 10000,再通過該地址即可訪問對應的內存單元,這種訪問數據的方式也稱為“間接訪問”

在了解上面這些原理之后,下面的語句就不難理解了:
/*指針的地址*/
printf("%x\n", &p);
/*指針保存的地址*/
printf("%x\n", p);
/*指針所保存的地址的值*/
printf("%d\n", *p);
以上面圖中的內存結構為例,對于語句“printf("%x\n”,&p)”,很顯然輸出的結果就是變量 p 的首地址編號 10006(即指針的地址);而對于語句“printf("%x\n”,p)”,輸出的結果就是變量i的首地址編號 10000(即指針保存的地址);而語句“printf("%d\n",*p)”輸出的結果就是變量 i 的值 100(即指針所保存的地址的值),它等價于語句“printf("%d\n",i)”。

最后來繼續溫習一下指針的 4 個基本概念。

1)指針的類型

從語法的角度看,只要把指針聲明語句里的指針名字去掉,剩下的部分就是這個指針的類型。這是指針本身所具有的類型,如下面的代碼所示:
int *p;            // 指針的類型是int*
int **p;            // 指針的類型是int**
int (*p)[3];      // 指針的類型是int(*)[3]

2) 指針所指向的類型
當通過指針來訪問指針所指向的內存區時,指針所指向的類型決定了編譯器將把那片內存區里的內容當作什么來看待。從語法上看,只需要將指針聲明語句中的指針名字和名字左邊的指針聲明符“*”去掉,剩下的就是指針所指向的類型。如下面的代碼所示:
int *p;             // 指針所指向的類型是int
char *p;            // 指針所指向的類型是char
int **p;             // 指針所指向的類型是int*
int (*p)[3];          // 指針所指向的類型是int()[3]
int *(*p)[4];       // 指針所指向的類型是int*()[4]

3) 指針的值
指針的值是指針本身存儲的數值,這個值將被編譯器當作一個地址,而不是一個一般的數值。在 32 位程序中,所有類型的指針的值都是一個 32 位整數,因為 32 位程序里內存地址全都是 32 位長。

指針所指向的內存區就是從指針的值所代表的那個內存地址開始,長度為 sizeof(指針所指向的類型)的一片內存區。所以當我們說一個指針的值是 XX 的時候,就相當于說該指針指向了以 XX 為首地址的一片內存區域;而我們說一個指針指向了某塊內存區域,就相當于說該指針的值是這塊內存區域的首地址。

4) 指針本身所占據的內存區
指針本身占了多大的內存?只要用函數 sizeof(指針的類型)測一下就知道了。在 32 位平臺中,指針本身占據了 4 字節的長度。

精美而實用的網站,提供C語言C++STLLinuxShellJavaGo語言等教程,以及socketGCCviSwing設計模式JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo