淺談C語言指針
學習指針,我們必須明白什么是指針。在大多數教材上給出的定義為:“一個變量的地址稱為該變量的指針”。
由此,首先我們來談談計算機內存地址。我們都知道運行一個C語言程序需要將程序從磁盤加載到內存中進行運算。而所謂的運算可以抽象理解為計算機處理器通過地址讀取指令、數據的過程。在內存中,每個字節(8位)對應有一個地址,連續存儲空間對應連續地址,處理器通過地址可得到存儲空間存儲的數值。為了方便理解,例舉出我們計算機存取變量值的原理:當我們定義一個局部變量例如short a=7時,系統會在棧中隨機分配一段字節數為2的連續內存,每個字節都會有地址。假設系統分配給變量a的地址為0x00-0x01,則這2個字節會分別存放00000000與00000111來表示7,當我們訪問變量a時,我們會得到變量a的首地址0x00,然后根據已定義的基類型(short型)取出相應字節數據
我們都知道32位處理器不支持4GB+運行內存,因為32位處理器可訪問的最大地址由8位十六進制表示,即最大訪問地址為0xffffffff,約為4G,超過4G內存,處理器訪問不到。
由此,我們明白了計算機地址的相關知識,在C語言中,存在內存空間中的某個數所對應的首地址就是這個數的指針,需要注意,指針均為十六進制表示的常量。因為在棧中內存是由系統隨機分配,所以我們不能直接對指針進行操作,由此引入一個變量專門來存放地址,這種變量稱為指針變量。我們都知道,C語言對數據進行訪問主要是通過兩種形式:1.直接通過變量名訪問(直接訪問)2.先得到指針變量中的值(地址),然后通過地址訪問。(其實二者本質上均為通過地址訪問,所謂的變量名在內存中不存在,是給編譯器看的,根據變量名訪問內存空間時,編譯器對照符號表(一種用於語言翻譯器中的數據結構,在符號表中,程序源代碼中的每個標識符都和它的聲明或使用信息綁定在一起,比如其數據類型,作用域以及內存地址)取出地址,訪問內存空間)
我們從以下4個方面深入了解C語言指針
通過指針引用變量(間接訪問)
指針變量是存放地址的變量,讓我們回顧一下什么是變量。變量:代表一個有名字的,具有特定屬性的一個存儲單元。它用來存放數據。從定義中我們可以得到兩個信息○1變量不是一個具體的值,而是一個存儲單元○2變量中存放的值可以改變
指針變量定義:基類型 * 變量名 例如int a.意思為定義a為一個指向int型數據首地址的指針變量,指針變量的值為地址
指針變量賦值:因指針變量值為地址,賦值為p=&a,將變量a地址賦值給p。我們給指針變量值賦地址,其最終目的是想通過地址操作存儲在該地址內存中變量的值。在C語言中,我們通常用指針變量名 表示指針變量指向的具體值。如point=1(給point指向的變量賦值為1)。當然,我們要將指針變量定義時的基類型 * 變量名 區分開,在定義中只表明該變量為指向基類型的指針
總所周知,變量除了可以直接操作之外,還可以作為函數參數進行傳遞,指針變量作為變量的一種自然也具備這一屬性,我們可以在其他函數中借助傳遞過來的變量地址對該變量進行操作
通過指針引用數組
要了解指針怎樣引用數組,我們先從最簡單的一維數組入手。通過數組來引用數組元素無外乎有兩種方式,一種是數組下標法,一種是指針法。在此,我們主要探究怎樣用指針法引用一維數組。首先一點我們必須明確,不管我們使用指針引用變量,數組還是函數。我們操作的都是他們的首地址。通過數組引用變量時,我們得到變量首地址,根據其聲明的數據類型從內存中取出相應字節。一維數組是一組相同類型、有序數據的集合,其數組名代表首地址,因此我們將數組名賦給指針變量的值,我們就可以通過指針變量引用該數組。舉個例子:在int a[]={1,3,5,7,9}中,我們該如何訪問數組中元素5.首先我們定義一個指針變量指向該數組int p=a。那么p+2即為a[2]的地址(數組是從a[0]開始),(p+2)即為a[2]的值5.需要注意這里的2,C語言編譯系統會自動換算為數組已定義類型再乘2,假設上述數組首地址為0x00,由於定義類型為int型占4個字節,計算機找到0x00+24=0x08中,並取出4個字節即為5
接下來,我們來探討二維數組,二維數組又稱為矩陣,為了形象化地理解二維數組,我們將二維數組寫成行列形式(實際為線性結構),前面講到我們對數組的引用都是通過首地址。那么,我們怎樣通過對首地址操作表示行與列。例如我們定義一個3行4列int型二維數組int a[3][4],3表示3行,4表示4列(第0行加1,表示第一行。第0行第0個元素加1,表示0行第一個元素),我們如何表示第二行的首地址呢?前面有說到用數組名來表示首地址,因此第2行首地址,我們可以用a+2來表示。我們如何表示第二行第二個元素的地址呢?是不是用(a+2)+2來表示?顯然不是,這種表示在一定程度上會給人誤解,因此,我們在指向行的指針前面添加一個,它們就成了指向列的指針,所以,我們很容易表示第2行第二個元素的地址(a+2)+2,進一步我們可以表示第2行第二個元素的值((a+2)+2)。數組在內存中是一段連續的內存空間,我們可以理解為第二行元素比第一行最后一個元素少一個基類型字節,因此第二行第二個元素的值也可以表示為*(a+10)。上面是用指針訪問。我們該如何用下標來表示行列的地址呢?其實很簡單,二維數組常被稱為“數組的數組”,比如上述a[3][4],我們可以將其視為一個以數組名a為首地址的a[3]表示行的數組以及分別以a[0],a[1],a[2],a[3]為數組名的int [4]列數組,故上述數組第2行第2個元素地址可以表示為a[2]+2,其值表示為a[2][2].
通過指針引用字符串
引用字符串有兩種方式:1.利用字符數組 2.用字符指針變量
例如1.char string[14]=”I LOVE CHINA!”;
2.char * string=”I LOVE CHINA!”;
第一種方式我們可以這樣理解,系統創建了一個14個字節的內存空間,將字符串逐個存放,最后一位存放‘\0’,數組名代表字符數組首地址
第二種方式中,由於C語言對字符串常量是按照字符數組來處理,但存入后,該字符數組沒有名字。因此不能通過數組名引用。只能通過指針變量來引用。上述第二條語句實際是將字符串首地址賦給string
通過指針引用函數
什么是函數指針?我們編譯一個函數,編譯系統為函數代碼分配一段存儲空間,這段空間起始地址稱為函數指針
定義:>int (*p)(int int)
引用:將擁有相同類型參數的函數名(起始地址)賦給p,p就可以調用該函數
最后再總結一下const關鍵字在指針中的規則:
Const int *p=&i:const在前,表示不能通過指針去修改變變量
Int *const p=&i:const在后,表示p這個指針不能再保存別的地址值