指針和數組存在着一些本質的區別。當然,在某種情況下,比如數組作為函數的參數進行傳遞時,由於該數組自動退化為同類型的指針,所以在函數內部,作 為函數參數傳遞進來的指針與數組確實具有一定的一致性,但這只是一種比較特殊的情況而已,在本質上,兩者是有區別的。請看以下的例子:
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<& lt;*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[])是一樣的,即函數列表中的數組此時退化成了指針