什麼是指針?
指針其實是記憶體單元位址
什麼是指針變量?
指針變量是用于存儲記憶體單元位址的變量
指針變量存儲的實質
顧名思義,指針變量存儲的是記憶體單元的值,即存儲的值其實指向的一個特定類型記憶體區域的起始位址。
如下例子,整型變量a的值為123,其記憶體單元位址為0x104,指針變量存儲的值是變量a的記憶體單元0x104。
指針變量指向了記憶體位址起始為0x104,長度為4個位元組的記憶體單元。p也可以對這個4個單元進行操作。

c語言中,我們可以像普通整型資料一樣,對指針變量進行運算。
指針變量的轉換
普通類型的指針變量都可以直接指派void *空類型指針,
但空類型指針需要強轉才可以轉成普通類型指針。
void 指針是一種特殊的指針,表示“無類型的指針”,因為其沒有指定類型,所有它可以指向任何類型的資料,也就是說任何類型的指針可以直接複制給void指針。
但是void指針指派給其他類型的指針時,必須強制轉換
這是因為指定了類型的指針變量指向了記憶體的一塊區域,但空類型指針無法确定指向記憶體區域的大小。
需要注意的是,不同類型指針變量互相轉換時,需避免通路非法的記憶體區域,導緻程式異常退出。
如下例子,chP指向了一段長度為1位元組的變量a的記憶體區域,當其強制轉換成int指針時,則超出了編譯器配置設定的記憶體區域,程式會異常退出。
指針變量的算術運算
指針變量可以使用算術運算符實作自增減,如下例
假設&a位址為0x100,則上面例子的輸出為
即使指針變量的算術運算為增減sizeof(資料類型)的大小。上例子中p的值為0x100,強轉為char 後自增1,結果為0x101,強轉為double 後自增1為,0x108(0x100+0x8)。
同理因為空指針類型無法得知其指向區域的長度,void *指針便無法進行增減操作。
C語言中,數組與指針是一種非常暧昧的關系,因數組和指針經常可以互相的轉換,是以經常會将其兩者混淆。
真正的事實是,兩者擁有不同的存儲結構,但引指針的靈活性,兩者可以互相的引用、轉換。
數組的存儲結構
與指針的存儲結構相比,數組在記憶體中占據的是連續的位元組單元。即指針存儲的長度根據計算機不同,是一個固定的大小 (32位4個位元組、64位8個位元組),數組存儲的是一塊連續的記憶體區域。
那為什麼指針可以通路數組中元素?
這是因為數組辨別符表示的是該數組的第一個元素的位址,當指針指向數組辨別符時,便可以通過指針通路數組中的各個元素。
函數調用時數組作為參數時為位址傳遞
C語言的标準中規定:所有的數組在作為參數傳遞時,都轉換成指向數組起始位址的指針,其他參數均采用值傳遞。
采用位址傳遞好處是形參不存在存儲空間,編譯系統不為形參配置設定記憶體,數組名或者指針便是一組連續空間的首位址。
例如下面例子輸出的size都為4。
數組和指針其實并不是一個相同概念,雖然在日常的使用中,經常可以使用指針代替數組,用于周遊數組的元素,例如
再來一個例子,可以證明數組并不是指針:
執行上面代碼會提示“a”: 重定義;不同的間接尋址類型
在使用extern int a時,編譯器認為a是一個在外部聲明的整型指針變量,但f1.c中,a是一個長度為3的整型數組,在32位系統系統下,int 長度為4位元組,而int [3]長度為4*3 = 12位元組。
再看一個例子
程式執行後列印different。這是因為a指向的是存儲于資料段靜态變量hello,arr則指向編譯器配置設定的記憶體空間。兩者的值并不一樣。
對于數組而言,編譯器已經為數組配置設定了一定的空間以及對應的位址,通過數組位址的偏移,可以通路該數組的元素。
而指針,編譯器為其配置設定了空間,用于存儲位址值。而對于存放在其中的值,隻有在程式運作時才能知道。
1. 使用指針前必須初始化,否則會指向錯誤的記憶體區域,導緻程式異常。
2.使用NULL指針作為函數調用失敗的傳回值。類似malloc函數,當配置設定記憶體失敗時傳回NULL,用以表示為一種異常情況。
3.在不知記憶體區域具體類型情況下,避免對void 指針進行算術操作
例如,下例子對p任何的操作都會導緻程式出錯
4. 不同類型指針轉換時,注意不超出編譯器配置設定的記憶體區域。
如下例子,ip使用了超出了編譯器配置設定的記憶體,會導緻程式異常退出
5.避免指針和整型資料的直接轉換。
1.指針變量是變量,存儲記憶體位址的變量。
3.數組存儲的是一段連續的記憶體區域
4.數組辨別符存儲了,一段記憶體區域的起始位址
5.數組作為參數傳遞時是位址傳遞,其他類型則為值傳遞