指針和數組存在著一些本質的區別。當然,在某種情況下,比如數組作為函數的參數進行傳遞時,由于該數組自動退化為同類型的指針,所以在函數內部,作為函數參數傳遞進來的指針與數組確實具有一定的一致性,但這只是一種比較特殊的情況而已,在本質上,兩者是有區別的。請看以下的例子:
?
char a[] = "1234567";
char *p = "1234567";
上述兩個變量的內存布局分別如下:
?
數組a需要在內存中占用8個字節的空間,這段內存區通過名字a來標志。指針p則需要4個字節的空間來存放地址,這4個字節用名字p來標志。其中存放的地址幾乎可以指向任何地方,也可以哪里都不指,即空指針。目前這個p指向某地連續的8個字節,即字符串"1234567"。
?
另外,例 如:對于a[2]和p[2],二者都返回字符‘i’,但是編譯器產生的執行代碼卻不一樣。對于a[2],執行代碼是從a的位置開始,向后移動2兩個字節,然后取出其中的字符。對于p[2],執行代碼是從p的位置取出一個地址,在其上加2,然后取出對應內存中的字符。
?
p指針變量本身在棧上,指向的內容在靜態存儲區;
?
a只是個數組名,本身不占運行時程序的空間,只是在源程序中用來標記一個字符數組(即其首地址),而數組也是存儲在棧上的。
?
?
char s1[] = "a";
?
char *s2 = "b";
?
a是在運行時刻賦值的;
?
而b是在編譯時就確定的;
?
但是,在以后的存取中,在棧上的數組比指針所指向的字符串(例如堆)快。
?
比如:
?
int main()
?
{
?
char a = 1;
?
char c[] = "1234567890";
?
char *p ="1234567890";
?
a = c[1];
?
a = p[1];
?
?
return 0;
?
}
?
對應的匯編代碼
?
10: a = c[1];
?
00401067 8A 4D F1 mov cl,byte ptr [ebp-0Fh]
?
0040106A 88 4D FC mov byte ptr [ebp-4],cl
?
11: a = p[1];
?
0040106D 8B 55 EC mov edx,dword ptr [ebp-14h]
?
00401070 8A 42 01 mov al,byte ptr [edx+1]
?
00401073 88 45 FC mov byte ptr [ebp-4],al
?
第一種在讀取時直接就把字符串中的元素讀到寄存器cl中,而第二種則要先把指針值讀到edx中,在根據edx讀取字符,顯然慢了。
?
#include<stdio.h>
#include<stdlib.h>
char *function1()
{
???? char *a="cdefgh";//在靜態存儲區分配,一直到程序結束
???? return a;
}
char *function2()
{
???? char a[]="cdefgh";//在棧中分配,函數結束時釋放
???? return a;
}
char a[]="cdefgh";
/*這個是常量字串的拷貝,
相當於strcpy(a,"cdefgh"),
這樣寫都會有字串拷貝,
造成時間和空間上的開銷,
如果字串很長儘量不要這樣寫,
由於字元陣列a在棧上,
所以在函數結束後它便無效了..
---------------
char *a="cdefgh";
a直接指向常量字串,
這個字串保存在靜態存儲區中...
所以在函數結束後,它返回的位址仍然有效..
*/
int? main()
{
??? char test[]="123";
??? test[0]='a';//可以修改數組內部元素的值
??? char* test1="456";
??? *test1='7';//test1不可以修改,是const char*的類型的值
??? /*char *a="cdefgh";
??? 此時a為const char*,
??? 也就是說你不能改變*a的值。
??? char a[]="cdefgh";你可以通過a[i]改變它的值。*/
??? char *i=NULL ;
??? char *j=NULL;
??? i= function1();//結吉確定
??? j= function2();//結果不確定
??? printf("/n%s/n",i);
??? printf("/n%s/n",j);
??? system("pause");
???
}
/*(1)function1()的a和function2()的a都是自動變數,都在棧上分配空間
(2)function1()的a分配的空間=sizeof(char *)=sizeof(void *),
?? 任何指標的大小都是相同的,指向靜態資料區存的"cdefgh"
(3)function2()的a分配的空間=strlen("cdefgh")+1,並且用來保存"cdefgh"
(4)返回的指標,function1指向靜態資料區,function1指向棧(已自動釋放)
?? 故function1的值是對的
*/
?
?
=============================================================
?
(1)指針數組:?是數組,但數組中的每個元素都是指針
int *p[5];//如p[2]是指針,可*p[ 2]=3;
(2)指向數組的指針:?是個指針,但它指向的是一個數組
int a[5];
int (*p)[5];//與前一行比較,*p相當于a,即p=&a;就像:int m;int *pm;//*pm就相當于m.pm=&m;
p= &a;//可與前一行合并為int (*p)[5]=&a;
----------------------
a代表這個數組,它是一個指針,指向第一個元素
這里一定要記住,a是數組名,&a代表的不是取a這個變量的地址,而是取數組元素的地址
---------------------------------------
a???? 的類型是 int[5]?????? 數組
&a??? 的類型是 int(*)[5]??? 指針——指向int[5]數組的指針
&a[0] 的類型是 int *???????? 指針——指向int類型的指針。
sizeof(a)=20;
sizeof(*a)=4 =sizeof(a[0]);
sizeof(*&a)=20;//因為p=&a,所以=sizeof(*p),而*p=a,所以=sizeof(a)=20;
---------------------------------------
int a[5]={1,2,3,4,5};
int *ptr=(int *)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));//輸出:2,5
指針加1要根據指針類型加上一定的值,不同類型的指針+1之后增加的大小不同,指針只是一個內存地址,但指針指向地址的長度可能不同,如char *pc與int *pi,sizeof(pc)=sizeof(pi)=4,但為什么輸出時,cout<<*pc只從內存處取一個字符,而cout<<*pi則從內存處取4個字符,這是由指針類型(char,int)決定的.對A* p;p+1=(A*)(p的地址值+sizeof(A)),如pc+1=pc+sizeof(char)=(char*)(pc的地址值+1個字節),而pi+1=pc+sizeof(int)=(int*)(pi的地址值+4個字節).
對代碼中的&a+1,&a是數組指針,其類型為int (*)[5],因為p=&a,也即是p的類型.所以&a+1=&a+sizeof(A),其中A為int[5]:(把A=int[5]代入A* p,即相當于int(*p)[5]).所以&a+1=&a的地址值+5*4字節,即變為數組a的結束地址的下一個地址(即&a[5]),&a+1仍是int (*)[5]類型,經(int *)(&a+1)強制轉化為int*,賦值給ptr.后面的ptr-1=ptr-sizeof(int)=ptr的地址值-4個字節,即變為數組a的最后一個元素的地址&a[4],*(ptr-1)即為a[4],故輸出為5.
而a+1=a+sizeof(int):(此處a退化為int*,故對于A* p,A為int)=a的地址值+4個字節,即是&a[1],則*(a+1)即a[1],故輸出2.
又如:
double t[]={1, 2, 578, 111,90} ;
double *pt= &t[3];//指向值為111
int *ptInt= reinterpret_cast< int * > (pt);
char *ptCh= reinterpret_cast< char * > (pt);
cout<< *( pt- 1)<< "\t"<< *(reinterpret_cast<double*>(ptInt-2))<<"\t"<<
*(reinterpret_cast<double*>(ptCh-8));//最后輸出結果全為578
---------------------
void Fun( int *p)與void Fun( int p[])是一樣的,即函數列表中的數組此時退化成了指針
?