一、一維數組
1、在內存空間上的存放
一個數組在定義后其在內存中各元素的存放是占據一段連續的地址空間,每個元素需要的空間取決於數組類型:整型需要4字節,字符型需要1字節。
示例:對於int a[100],在內存中占用100×4=400字節空間;對於char c[100]占據內存空間為100字節。
2、數組名
2.1數組名是什么?
首先數組名是數組的名字,所以數組名表示該數組=。=這絕不是廢話,這句話非常有助於后面的理解。
其次,數組名還能表示什么?我們都知道,數組名能表示一個地址,如何描述該地址,先直接給出答案:數組名還表示該數組首元素的地址=。=(依然不是廢話,而是要注意到:是該數組首元素地址,而不能完全等同於該數組的首地址,后面會細說)。
2.2數組名、數組元素與其取地址的關系
看一個具體例子:int a[10];
先梳理一下,a是數組名,a表示該數組首元素首地址,a[0]表示第0個元素,&a[0]表示首元素的首地址。很自然:a=&a[0]。
繼續多想一下,&a是什么?因為a是該數組,所以&a是該數組的首地址。很自然:a=&a[0]=&a.
測試下:
#include<stdio.h> int main() { int a[10]; printf("%p\n%p\n%p",a,&a[0],&a); getch(); return 0; }
三者在數值上相等,那么三者之間的區別在於什么呢?
首先看&a和&a[0];
a是一個數組,a[0]是數組的一個元素。對兩者取地址操作,雖然操作后的數值相同,但兩種地址的指向含義是不一樣的;&a指向數組單位;&a[0]指向數組元素單位,兩者的單位不同,大小也不一樣。
說明這里單位空間的含義,假設X為n字節大小的數據,那么&X的含義是(n字節空間的首地址)。對於int m;則&m表示的是一個整型數據大小空間(即4字節)的首地址,(&m+n)表示在m首地址上加n個單位大小的內存空間,設&m=0x10000000H;則&m+1=0x10000004H;&m+10=0x10000028H.
那么在這里,很顯然a和a[0]大小不同,a的大小是10×4=40個字節,a[0]大小是4個字節。具體可以通過(&a+n),(&a[0]+n)進行驗證測試:
#include<stdio.h> int main() { int a[10]; printf("%p\n%p\n%p\n%p\n%p\n%p",a,&a[0],&a,&a[0]+1,&a[0]+2,&a+2); getch(); return 0; }
弄清楚&a和&a[0]之后,繼續回到數組名a上。
回顧數組名的兩層含義:
第一層:表示一個數組。為何說這么明顯的一句話不是廢話?因為理解了數組名是一個數組,就很輕松可以理解&a:對一個數組取地址,而非對一個數組元素取地址,這可以幫助理解之前&a和&a[0]的區別。
第二層:表示該數組首元素的地址。那么依據之前分析的,(a+n)表示什么?要看(a+n)表示什么,首先要看a表示的是什么,a表示的是首元素地址,那么該地址空間的單位大小就是數組元素的大小,則(a+n)表示數組第n個元素的地址,繼續測試:
#include<stdio.h> int main() { int a[10]; printf("%p\n%p\n%p\n%p\n%p\n",a,&a[0],&a,a+1,a+3); getch(); return 0; }
這里仍然要重點說一下的是:我認為理解&a和&a[0]的相等性和差異是必要而且從邏輯上是合理的,而對於a的第二層表示地址的含義,無法從邏輯上合理得出,而只能去記住:a作為地址含義時,表示的是數組首元素地址,而非數組首地址,並且:數組名非變量,不能作為左值,不能自增自減。從數組定義開始,數組名所表示的地址就已經被固定了,無法修改。
3、指針變量在數組中的應用
回顧數組在內存中的分布:依次連續的一段空間。並且可以通過下標方式去訪問數組元素。
對於char c[10];c[0]表示第0個元素,c[i]表示第i個元素。該數組在內存中占據10字節空間,如何細化描述這段空間:該數組在內存中占據10個單元空間,每個單元都是char型數據,為1字節。
定義一個指針變量並使其初始指向字符數組首元素地址:
char *pc=c;
容易理解:pc+n指向第n個元素,即*(pc+n)==a[n];
同時,一樣可以通過對pc的下標來進行訪問元素,對於上例中:p[i]=*(p+i)=a[i]=*(a+i);&p[i]=(p+i)=(a+i)=&a[0]+i;
這里說明一下:C編譯器當遇見下標時候,比如X[i],先判斷X是否表示一個地址(數組名或取地址運算或指針類型均可),再判斷X是一個指向什么類型的地址(通過判斷類型,來得出單位長度);通過上面兩步,設X指向類型單位長度為m,把下標形式轉換為(X+i*m)來進行定位。
再引申:
(1)char *pc=c可以替換為char *pc=&c[0]么?
當然可以。
(2)char *pc=c可以替換為char *pc=&c么?替換后(pc+n)表示什么呢?
一樣可以替換,(pc+n)依然表示a[n]的地址。
這里有可能疑惑:&c不是表示整個數組的首地址么?。(&c+n)表示的是數組c首地址+n*sizeof(c),那(pc+n)不應該也是數組c首地址+n*sizeof(c)嗎?
這里要注意pc是什么?pc是字符指針,所以pc表示的單位大小還是字符型,單字節大小。pc=&c這里只是賦值運算,只是把&c的數值賦給pc。而pc表示的單位大小仍然為單字節。所以(pc+n)依然表示a[n]的地址。
一個關於數組元素訪問的綜合示例如下:
#include<stdio.h> int main() { char c[10]="abcde12345"; char *p1=c; char *p2=&c[0]; char *p3=&c; int i; int s1=sizeof(c[0]); int s2=sizeof(c); printf("%d-%d\n字符各種訪問方式如下\n",s1,s2); for(i=0;i<10;i++) { printf("%c--%c--%c--%c--%c--%c--%c--%c\n",c[i],*(c+i),p1[i],*(p1+i),p2[i],*(p2+i),p3[i],*(p3+i)); } getch(); return 0; }