指針與數組(一)

問題1:指針的內存分佈

double *p

我們知道是定義了一個指針變量p,而在sizeof中也知道是一個 "double*"類型的模子在內存中咔嚓出4個字節的空間,然後將這個空間命名爲p,但同時限定這4個字節的空間裏只能存儲摸個內存的地址,即使你存入別的任何數據,都被他當做地址處理,而且這個內存地址開始的8個字節上只能存儲某個double類型的值。對這個空間的訪問是匿名的,只能通過指向他的指針變量通過*這個開鎖工具來獲取它。

問題2 :int *p=NULL與 *p=NULL一樣嗎?怎樣向指定內存賦值?

利用監視窗口監視下面p值

int * p=NULL;

這時候我們可以查看p的值爲0x000000000,這個代碼的意思就是,定義一個指針變量p ,其指向的內存裏邊保存的是int類型的數據,在定義變量p的同時將p的值設置爲0x00000000,這個過程是初始化,也是我們強調的,在定義時候養成初始化的習慣,因爲如果不初始化是很嚴重的事情,可能p指向的內存會造成非法操作。

而 int*p;

*p=NULL:

這時候定義了一個指針變量,但這時候該變量的值是多少不知道,也就是說現在p保存的可能是一個非法地址(實際上編譯器會不讓其編譯通過的),而第二行是將p指向的內存賦值爲NULL,所以設想如果真讓你通過那計算機的世界就混亂了,這樣你都可以隨意去修改任何地方的數據了,並且其實計算機中也有一些地方的地址進行保護的,所以也是不允許你隨意的向一個指針變量賦值去操作。那麼,應該怎樣向指定的內存地址去操作呢?

看例子:

int i=10;

int *p=&i;

*p=NULL;

我們應該可以從這裏獲取靈感,那就是爲了確認某個內存是我們可以操作的,可以先int i一個,然後通過監視窗口看下他是多少,例如我這裏是0x12ff7c,那麼我們就可以這樣操作:

int *p=(int *)0x12ff7c

注意這裏是將地址0x1277ff7c賦值給指針變量p的時候必須強制轉化。

問題3 :數組的內存佈局

看例子

int  a[5];

我們都知道這裏是定義了一個數組,該數組包含了5個int型數據,我們可以用a[0],a[1]等來訪問數組中的每個元素,但是一定要明白,a[0],a[1]這些都不是元素的名字。

當我們定義 一個數組a時候,編譯器根據指定的元素個數和類型分配確定大小的一塊內存(這裏是4*sizeof(int)),並把這塊內存命名爲a。a[0],a[1]等都是a的元素並非元素的名字,數組中的每個元素是沒有名字的。回顧我們前邊講sizeof時候  sizeof(a[5])會返回4 不出錯,這是因爲sizeof是關鍵字,不是函數,函數求值在運行時候,而關鍵字sizeof求值是在編譯時候,雖然不存在a[5]但是這裏也沒有真正去訪問a[5]而是僅僅根據了數組元素的類型來確定其值。

問題4:(重點) &a與 a——a其實是等於&a[0]

一定要注意,雖然你測試&a與a的值是一樣的,但是其意義大不相同,先通過下面例子來看

int main(int argc, char* argv[])
{
    int a[5]={1,2,3,4,5};
	int *p=(int*)(&a+1);
	printf("%d,%d,%d\n",*(a+1),*(p-1),sizeof(&a));
	return 0;
}
這裏輸出的結果是 2,5,20

爲什麼呢?

這是因爲a表示的是數組首元素的首地址,而不是數組的首地址!!!!a作爲右值意義與&a[0]是一樣的。而&a表示的是數組的首地址。

所以對於這個例子,

&a+1是表示取數組的首地址,而我們知道對一個地址加1加的是這個類型的偏移,前邊我們在說數組內存分佈時候已經知道,我們是根據元素類型跟大小分配一塊確定的內存取名爲a,所以這個數組是一整個數據模子,所以對&a加1就是移動到下一個數組處(同時注意這裏的類型強制轉化),也就是指向了a[5],雖然這裏不存在

&(a+1),這裏表示的是取首元素的首地址,然後對其地址偏移一個單位,這時候就是移動到了下一個元素即a[1]

*(p-1) 因爲這裏指向了a[5],而p又是被強制轉爲int*,所以 *(p-1)指向了a[4]

所以,再次明確強調一下:

a 表示的是數組a的首元素的首地址,與&a[0]意義一樣;

&a 表示的是數組a的首地址,是否還記得在sizeof中我們利用宏實現sizeof的我們這樣寫

#define SIZEOF(var)  ((char*)(&var+1)-(char*)(&var))

#define SIZEOD(type)  ((char*)((type*)0+1)-(char*)((type*)0))

所以明白這裏爲什麼這麼寫了吧!

問題5: 數組與指針之間的朦朧相似

5.1 以指針或下標形式訪問指針

char *p="abcdef";

這裏定義了一個指針變量p,p 本身在棧上佔有4bytes,p指向的那塊內存在靜態區,其大小爲7,這塊內存也沒有名字,對這塊內存的訪問只能是匿名的,比如讀取'e',有兩種方式

1) 指針形式 *(p+4)

2)以下標形式 p[4],編譯器總是把以下標形式的操作解析爲以指針的形式,p[4]會這樣被解析;先去得p中存儲的地址值,而後加上中括號中4個元素的偏移值,計算出新的地址,從而訪問,所以實際上下標的形式訪問與指針形式是一樣的,他還是會將他解析成指針形式。所以這兩種方式在訪問上沒有本質的區別,只是寫法的不同罷了。

5.2 以指針或下標形式訪問數組

char a[]="123456"; 定義了一個數組a,a擁有7個char類型的元素,其空間大小爲7,數組a本身在棧上。對於a的元素的訪問必須先根據數組名a首元素的首地址,然後加上偏移量找到對應的值,這是一種”具名+匿名“訪問。現在我們要讀取字符'5'.依舊可以有指針域下標的兩種形式

1)指針形式: *(a+4);  a這時候代表數組首元素的首地址,然後加上4個字符的偏移量,這樣就得到了‘5’所在的地址,再用*取出該位置上的值。

2)以下標形式 a[4] 這種應該是我們最常用的一種方式,編譯器總是把以下標形式的做操作解析爲以指針的形式的操作,所以a[4]的操作會被解析爲:a是數組首元素首地址,加上中括號的4個元素偏移量,計算出新的地址,然後從新的地址中取出值

由上面所述,指針與數組根本是兩個完全不同的東西,只是他們都可以以”指針形式“與”下標形式“來訪問,


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章