1. 程式人生 > >C語言學習筆記——指標

C語言學習筆記——指標

1.簡單指標  *p:

(1)用於陣列

int *p 定義指標

該指標表示指向某個變數的地址;

當指標與自增符號結合時,簡單舉例如下:

int m[5]={1,3,5,7,9};p=m;

A.*p++;     B.*++p;    C.++*p;    D.x=*p++;   E.x=*++p;    F.x=(*p)++;   G.x=*(p++) ; 

首先,看個簡單的例子,int m=0,n;   n=m++; n=++m;  n=(m++);中第一個式子得到m=1,n=0第二個式子得到m=1,n=1.

其中m++和++m的區別在於是 先賦值還是先計算。易搞混的是第三個式子n=(m++)得到的結果仍然是m=1,n=0 此處須說明,雖然括號將m++擴起優先計算,但是在C中,m++的含義是在計算其他賦值等式子之後再單獨進行++,可以理解為單獨一句m=m+1,因此優先順序考慮就與擴號無關了。

由此再看ABC:先來比較*與++的優先順序:*做乘號,比++優先順序低,但是與此處無關;*做取內容符號時,優先順序同++p中的++,但是低於p++中的++。

在AB中,二者意思相同,均為將指標後移一個單位,A中容易混淆。須記住它不是 *p = *p + 1; 它卻是 *p = *(p+1); 而且是後加加;由於++和*優先順序相同,C選項中先計算*p,取到p指向的內容,再對內容做自增,與前二者不同,程式碼如下:

#include<stdio.h>
main() 
{
	int m[5]={1,3,5,7,9},*a,*b,*c;
	a=m;
	b=m;
	c=m;
	*a++;
	*++b;
	++*c;
	printf("this is *a: %d \n",*a);
	printf("this is *b: %d \n",*b);
	printf("this is *c: %d \n",*c);
}
輸出結果見下



對於DEFG,結合上面兩個方面也就不難理解:

D先將p指標內容給x,隨後指標自增,得到*p為3,x為1;

E中先指向下一位再賦值,x=*p=3;

F中x仍為指標原本指向的值x=1;此處先取到p的值,再對值自增,得到*p最後為2;同時m[0]的值同步變為2;

G中由上方分析可知,()的存在對式子和++的先後順序無影響,結果同D。


(2)用於字串

字串中,指標在用法上與陣列有很大區別:

a.指標指向字串須與字元串同資料型別;

b.講指標指向字串後,指標即可代替該字串進行使用,表示整串輸出字串內容,而不是地址,如用puts(指標名)直接輸出整個字串;

c.二者用取內容運算子*,均表示相應陣列在對應位置的內容,字串指標p的型別是string,*p則是char(具體應用在輸出格式上),注意,字串指標*p指單個char變數,判斷是否結束,用*p==‘\0’;

d.一個易錯點!:

如下寫法會正常編譯,但是在執行時出現卡死:

int main() {
	char *s1="abc",*s2="def";
	s1[2]=s2[1] ;//這裡出錯

	return 0;
}:
如果把字串指標當做陣列來用,必須先定義已有的陣列,再令字串指標指向該陣列,否則會出錯。正確寫法:
int main() {
	char s1[]="abc",s2[]="def";
        char *p1=s1,*p2=s2
	p1[2]=p2[1] ;//這個時候才能把字串陣列當指標用

	printf("%c",s1[2]);
	return 0;
}


2.指標陣列*p[],陣列指標(*q)[]:

     二者被我多次搞混,出現語法等錯誤,故放在一起討論

     *p[n]表示的是一個指標陣列,從符號優先順序可以看出來,其和*(p[n])等價。即一個指標的集合,包含n個指標,他們相互獨立。這種用法與二級指標**p相似

其常見應用:表示二維字元陣列:

int main( )
2.{  char  *p[ ]={"PROGRAM","BASIC","C","JAVA"};
3.   int    i;
4.   for (i=3;i>=0;i--)  printf("%c",*p[i]);
5.   printf("\n"); 
6.return 0;
7.}
結果:JAVACBASICPROGRAM

其結果為倒敘輸出四個單詞,char *p[n]表示一串指標時 等價於 n個char *p 等價於char  **p(二級指標詳細部分見下)



另外 用二維整型陣列表示*p[n]容易出現的一種錯誤,剛好與(*q)[n]做簡單對比:

int main(int argc, char *argv[]) {
	int a[3][4]={1,2,3,4,
				5,6,7,8,
				9,10,11,12},i,j;  
	int *p[3];        //這裡的角標是3,因為這是一個含有3個指標的陣列,每個指標對應上面二維陣列的一行 
	*p=a;             //這裡用的*p,區別於下面。因為這裡的p相當於二級指標,對應的*p才是一級指標,指向陣列
	for(i=0;i<3;i++)  
	    for(j=0;j<4;j++)  
	        printf("%3d",a[i][j]) ;  
	printf("\n");
	for(i=0;i<3;i++)  
	for(j=0;j<4;j++)  
	    printf("%3d",*(*(p+i)+j)) ;
	
}

此處在輸入12個數之後  得到的結果只有前四個與上吻合,原因是指標陣列內部變數相互獨立。輸出如下:




下面再來看 (*p)[n]

如int(*P)[10],p先和*結合,意味著p是一個指標,他指向int [10],即p是一個指向一個數組的指標

int main(int argc, char *argv[]) {
	int a[3][4]={1,2,3,4,
				5,6,7,8,
				9,0,1,2},i,j;  
	int (*p)[4];     //注意這裡角標是4.原因是這個指標指向int [4]的陣列
	p=a;             //注意這裡直接p=a,區別上面的*p=a;因為這裡的p是一級的指標,指向陣列
	for(i=0;i<3;i++)  
	    for(j=0;j<4;j++)  
	        printf("%3d",a[i][j]) ;  
	printf("\n");
	for(i=0;i<3;i++)  
	for(j=0;j<4;j++)  
	    printf("%3d",*(*(p+i)+j)) ;
	
}

注意二者在表示上略有不同 此處n=4;等於二維陣列的列寬;另外在賦予地址時,此處不需要間接引用符*,只需將q指標指向陣列第一個記憶體位置即可。
輸出結果如下:




3.二級指標**p

二級指標表示指向指標的指標,可以理解為指標陣列*p[]


假如如此定義:int **p,(*q)[5];

這時的*(p+i)和q[i]是等價的;

int  main( )
{  char *a[6]= {"ABCD","EFGH","IJKL","MNOP","QRST","UVWX"};
   char      **p;
   int       i;
   p=a;
   for( i=0; i<6; i++ )    printf("%s",*(p+i));
   printf("\n");
   return 0;
}
如上,其中的*(p+i)就等價於a[i],等價於int *q="...."

二級指標一個很重要的用法就是在呼叫函式過程中使用,使在函式內部可以改變原先指標的指向:

void change_ptr(void **ptr, void *dest)

{

     *ptr = dest;

}
change_ptr(&ptr,dest)
注意此處呼叫時  要取二級指標對應項的地址,才能在函式內部使二級指標指向指標,通過二級指標改變指標本身的內容。
可以這樣改變指標內容是因為,C中進入一個子函式的時候,會預設把原函式中需帶入的變數拷貝一份進入子函式;這時拷貝一個指標的地址,等於在函式中可以改變這個指標本身

關於二維陣列和二級指標,指標陣列相關,可以參考下面這個文章:

點選開啟連結



4.指向結構的指標

指向結構的指標用p->xxx來表示,其中xxx是結構內部定義的一個項;

如定義struct list{}a此時 p->xxx等價於 a.xxx等價於*(p).xxx





補充部分:

1.關於free(q): free和malloc要成對出現

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main()
{
  char *str = (char *)malloc(100);
  strcpy(str, "hello");
  free(str);
  if(str != NULL)
    {
      strcpy(str, "world");
      printf("%s\n", str);
    }
      return 0;
}

free後的指標,要及時進行NULL,否則會成為野指標,影響穩定性;

另外free()不能用在這種情況下:

int *p=0;
p=malloc(100*1024);
p++;
free(p);
free()必須釋放指定分配的記憶體,不可以是進行移動、賦值後的指標。


2.關於兩字串相減

C中把字串認為是指標,字串相減就是指標相加減,如

char s1="abcdef",s2="def";

int n=s2-s1;

得到的值n是很大的數,因為兩字串地址不相連


第二種情況

char *s1="abcdefg",*s2;

s2=s1;

s2+=5;

int n=s2-s1;

這裡得到的n就是二者地址差,因為二者的字串內容在同一段地址上,s2指標在後移之後是"fg",二者地址差是5.


第三種情況:兩個指標相減:

int a[]={2,4,6,8,10,12};
int *p1 = a;
int *p2 = &a[3];
printf("p2-p1=%d",p2-p1)
這裡可能有如下兩種結果:

  I.得到的是二者地址差,二者差3個int型的地址,也就是3*4=12或3*8=24;

 II.得到的就是二者指向位置之差,即3.

經過編譯運算,這裡是第II種情況。指標相減不能得到地址差,而是得到“地址差÷sizeof(資料型別)”。



3.指標用於多個返回值

 指標可用於函式中來返回多個值,具體用法:

設定swap函式,交換兩個變數的值:

void swap(int *x1,int *x2);
int main(int argc, char *argv[]) {
	int a,b;
	scanf("%d",&a);
	scanf("%d",&b);
	swap(&a,&b);
	printf("a=%d,b=%d",a,b);
	return 0;
}
void swap(int *x1,int *x2){
	int temp;
	temp=*x1;
	*x1=*x2;
	*x2=temp;
}
 函式部分曾經錯寫為:

int *temp;temp=x1;x1=x2;x2=temp;

這裡的交換是交換過程操縱兩個指標變數,修改對應地址的內容,而不是對指標進行交換,函式內部的交換在返回main時失效。



4.不要這樣寫指標

int *p=5;
 這種寫法容易使程式崩潰。道理很簡單:當我們定義一個指標int * p 的時候,這個指標會隨機指向某個位置,如果我們這時修改指標指向的記憶體,使之變為“5”這個數字,那麼該地址中原本的內容會被覆蓋掉,如果是一些重要資料,則會導致崩潰。因此一般情況下,定義指標後要用已經定義的變數去“引導”指標指向固定好的位置。
int a = 5;
in *p = a;

此外,在C中,陣列p[]和指標*p在C中是同一個東西,即陣列是一個常量指標,同時定義陣列p[]和指標*p就會出現衝突錯誤。

易錯點:

當對一個指標進行自增操作(q++)時,不是地址值+1,而是地址值+n。如:

 int *p ,則p++表示地址+4/+8;

 char *q,則q++表示地址值+1。


另外,注意區分:*(p+1)和 *p + 1。二者區別一個是地址++,一個是內容++。


 5.const 與 指標

有如下三種寫法:

int a;
const int *p1=&a;  //寫法1
int const *p2=&a ;     //寫法2
int *const p3=&a ;     //寫法3
這裡三者區別,只看 const 與 * 誰前誰後:

const 在 * 之前,p可以改變地址,但是不能利用p去改變指向處的變數值,如1,2;

const 在 * 之後,不可變的是指標p,因此指標p指向的地址不能變,如3;

另外,三者對變數a無影響,仍然可以直接通過a修改內容。



以上個人筆記,如有錯誤請指出,會持續進行補充