一維數組與指針
int a[10];
int *pa;
一維數組的數組名代表的就是該數組第一個元素的地址,所以復制語句pa=&a[0]等價於pa=a。對數組元素a[i]的引用等價於*(a+i)。在計算數組元素a[i]的值時,C語言實際上先將*(a+1)
後再進行求值,所以用指針編寫的程序比用數組下標編寫的程序執行速度快。
數組名和指針之間有一個不同之處,指針是一個變量,pa=a和pa++都是合法的。但數組名不是變量,因此,類似於a=pa和a++形式的語句是非法的。
當把數組名傳遞給一個函數時,實際傳遞的是該數組第一個元素的地址。在被調用函數中,該參數是一個局部變量,因此,數組名參數必須是一個指針。
在函數定義中,形式參數char s[]和char *s是等價的。通常使用後一種形式,因為它比前者更直觀地表明了該參數是一個指針。
字符數組和字符指針
char a[] = "Hello";
char *p = "Hello";
a是一個僅僅足以存放初始化字符串以及空字符’\0’的一維數組。數組中的單個字符可以進行修改,但a始終指向同一個存儲位置。
p是一個指針,其初值指向一個字符串常量,之後它可以被修改以指向其他地址,但是不能修改字符串的內容。
char *p = “Hello”,”Hello”保存在靜態數據區,該數據不能修改。由指針m指向,不能通過指針m來修改靜態數據區的值。
char a[] = “Hello”,”Hello”保存在棧空間數組裡。 數組名為a, 變量名為數組的首地址。可以通過a[i]=’a’或*(a+i)=’a’的形式來修改數組內容。
指針與多維數組
比較兩個定義
int a[10][20];
int *b[10];
從語法角度講,a[3][4]和b[3][4]都是對int對象的合法引用。但a是一個真正的二維數組,它分配了200個int類型長度的存儲空間,並且通過常規的矩陣下標計算公式20*row+col計算得到元算a[row][col]的位置。
但是對b來說,該定義僅僅分配了10個指針,並且沒有對它們初始化。假定b的每個元素都指向一個具有20個元素的數組,那麼編譯器就要為它分配200個int類型長度的存儲空間以及10個指針的存儲空間。
指針數組的優點
數組的每一行長度可以不同,b的每個元素不一定都指向一個具有20個元素的數組。
二維數組和二維指針
二維數組和二維指針的概念很容易混淆,請看下面這段代碼。
#include
void print_str(char** strs, int i)
{
printf("strs[%d]=%s", i, strs[i]);
}
int main(int argc, char **argv)
{
char strs[10][100] = {"Hello", "World"};
print_str(strs, 0);
return 0;
}
執行這段程序,會得出現Segmentation Fault。
Segmentation Fault是由於程序試圖訪問不該訪問的內存,詳細介紹見Wikipedia
https://en.wikipedia.org/wiki/Segmentation_fault
變量strs實際為一個類型為char (*)[100]
的指針,指向二維數組的第一個元素,即一個包含100個char的數組,指針的值實際上也等於二位數組第一個char元素的地址。print_str函數的參數聲明為char**
,即二級指針,參數傳遞不會改變指針的值。所以strs的值跟main中定義的strs的值是一樣的,不過print_str中的strs是二級指針。那麼str[0]則是一級指針,strs[0]=*(strs+0),那麼strs[0]的值為字符串“Hell”所占四個字節的值對應的地址,“Hell”對應的ASCII碼為0x48 0x65 0x6C 0x6C, 組成的地址為0x6C6C6548(little endian),而此處內存禁止訪問,所以出現了Segmentation Fualt。