C語言指針簡介與相關的難點

本篇我主要寫了指針簡介以及指針與簡單數組,二維數組的關係
由於字符數組用的比較廣,問題也比較多,所以我會單獨再出一篇關於指針與字符數組的
最後感謝大家的觀看,如果大家能有所收穫,那真是榮幸之至

一、指針簡介

1.什麼是指針

  • 地址和變量:在計算機內存中,每一個字節單元,都有一個編號,稱爲地址
  • 指針:在C語言中,內存單元的地址稱爲指針,專門用來存放地址的變量,稱爲指針變量

2.指針變量的作用

  • 使程序簡潔、緊湊、高效
  • 有效地表示覆雜的數據結構
  • 動態分配內存
  • 得到多於一個的函數返回值

3.指針變量的聲明及使用

1)聲明的一般形式如下:
<數據類型>  * <指針變量名> ; 
 例如:  char  *pName ;
  • 指針說明時指定的數據類型不是指針變量本身的數據類型,而是指針目標的數據類型。簡稱爲指針的數據類型。
2)指針初始化
  • 指針在說明的同時,也可以被賦予初值,稱爲指針的初始化,一般形式是:
<數據類型>  *<指針變量名> = <地址量> ; 
 例如:int  a,  *pa = &a;
 在上面語句中,把變量a的地址作爲初值賦予了剛說明的int型指針pa。等價於  int *pa;  pa = &a;

在這裏插入圖片描述

  • 上圖中,x的值爲256,x的地址爲0x102ff80,執行完p = &x 後,p裏面存儲的是a的地址,而它自己本身也有一個地址,即0xb102ff80。
  • 其中值得注意的是int類型其實佔4個字節,實際中如果一個類型佔用的字節數大於1,而變量的地址就是地址值最小的那個字節的地址,爲了簡略,我只畫了一個字節來表示一個int類型,這其實是錯誤的,這樣畫只是爲了讓大家更容易理解,希望不要誤導了大家。
3)指針的目標

如果它指向的區域是程序中的一個變量的內存空間, 則這個變量稱爲指針的目標變量。 簡稱爲指針的目標。

4)p,*p和&p的區別

引入指針要注意程序中的p、*p 和 &p 三種表示方法的不同意義。設p爲一個指針,則:

  • p — 指針變量,它的內容是地址量
  • *p — 指針所指向的對象,它的內容是數據
  • &p — 指針變量佔用的存儲區域的地址,是個常量
int a = 50;
int *p ;
p = &a; 
	
printf("&a = %p\n",&a);    //取a的地址 
printf("p = %p\n",p);      //取指針p的值 
printf("&p = %p\n\n",&p);  //取指針p的地址 

//由下面三時可以看出*p,a,*(&a)的關係 
printf("*p = %d\n",*p); 
printf("a = %d\n",a); 
printf("*(&a) = %d\n",*(&a)); 

由結果可知,p=&a即p中存的值就是a的地址,而&p則表示p他自己所在的地址
同時,還可以看出 *p,a, *(&a)三者等價
在這裏插入圖片描述

5)指針的大小

指針存放的是地址,它的大小與CPU位數,操作系統位數和編譯器位數有關。 CPU只是影響指針大小的首要因素,除了它之外還要看操作系統和編譯器的位數。這裏指針的大小由這三個東西中位數最小的那項決定。 比如,如果CPU、系統都是64位的,但編譯器是32位的,那麼很顯然指針只能是32位4字節大小。

int ia ,*ip;
double da ,*dp;
char ca ,*cp;
short sa ,*sp;
	
printf("int = %d ; *int = %d\n",sizeof(ia),sizeof(ip)); 
printf("double = %d ; *double = %d\n",sizeof(da),sizeof(dp));
printf("char = %d ; *char = %d\n",sizeof(ca),sizeof(cp));
printf("short = %d ; *short = %d\n",sizeof(sa),sizeof(sp));

在這裏插入圖片描述

6)指針賦值運算常見的有以下幾種形式:
  • 把一個普通變量的地址賦給一個具有相同數據類型的指針
double  x=15,  *px;   
px=&x;
  • 把一個已有地址值的指針變量賦給具有相同數據類型的另一個指針變量.
float  a, *px, *py; 	
px = &a;		
py = px;
  • ·把一個數組的地址賦給具有相同數據類型的指針。
int  a[20],  *pa;
pa = a;   //等價 pa = &a[0]

二、指針的運算(一般與數組相結合)

  • 指針運算是以指針變量所存放的地址量作爲運算量而進行的運算
  • 指針運算的實質就是地址的計算
  • 指針運算的種類是有限的,它只能進行賦值運算、算術運算和關係運算

1.指針加減一個n的運算: px +/- n

  • px+n表示的實際位置的地址量是:
    (px) + sizeof(px的類型) * n
  • px-n表示的實際位置的地址量是:
    (px) - sizeof(px的類型) * n

2.兩指針相減運算

注意:不同數據類型的兩個指針實行加減整數運算是無意義的

  • px-py 運算的結果是兩指針指向的地址位置之間相隔數據的個數。因此,兩指針相減不是兩指針持有的地址值相減的結果。
  • 兩指針相減的結果值不是地址量,而是一個整數值,表示兩指針之間相隔數據的個數

3.指針自加一、減一運算

  • 該運算與c語言原始的++,–類似,實質還是上面的+1或-1,但使用時,要注意是先運算還是先賦值,注意運算順序

4.指針關係運算

  • 兩指針之間的關係運算表示它們指向的地址位置之間的關係。指向地址大的指針大於指向地址小的指針。
  • 指針與一般整數變量之間的關係運算沒有意義。但可以和零進行等於或不等於的關係運算,判斷指針是否爲空。
	int a[]={1,2,3,4,5,6,7,8};
  	int *p,*q;
	p=a;//等價於 p = &a[0]
	q=&a[3]; 
	
	//判斷p是否指向a[0],q是否指向a[3]
	printf("p:%p  *p=%d\n",p,*p); 
	printf("q:%p  *q=%d\n",q,*q); 
    
    //判斷p+3是否指向a[3]
	printf("p+3:%p *(p+3)=%d\n",p+3,*(p+3));//讀p+3數據時注意加括號,否則結果就大不一樣了 
	
	//判斷兩指針相減是否表示兩指針間相差的元素個數
    printf("q-p:%d\n",q-p);

    //判斷指針++的運算順序
	q = p++;     //p先賦值給q,q在執行自加 
	printf("p:%p  *p=%d\n",p,*p); 
   	printf("q:%p  *q=%d\n",q,*q); 

在這裏插入圖片描述

三、指針與數組

  • 在C語言中,數組的指針是指數組在內存中的起始地址,數組元素的地址是指數組元素在內存中的起始地址
  • 一維數組的數組名爲一維數組的指針(起始地址)
    例如:double x[8];
    因此,x爲x數組的起始地址

x[i] 、(px+i)、(x+i) 和px[i] 四者關係

設指針變量px的地址值等於數組指針x(即指針變量px指向數組的首元數),則:x[i] 、(px+i)、(x+i) 和px[i] 具有完全相同的功能:訪問數組第i+1個數組元素。
在這裏插入圖片描述

注意,使用p[i]這種表示數組元素時有一定侷限性,使用時一定要注意p的地址是什麼

	int a[] = {2,4,6,8,10};
   	int *p = a;
	int n = sizeof(a)/sizeof(a[0]),i;
	
	for(i = 0; i < n; i++)
		printf("%d %d %d %d\n",a[i],*(p+i),*(a+i),p[i]);//四種方法等效
		
	p++;
	printf("%d %d\n",a[1],p[1]); //p++後,p表示a[1]的地址,故此時p[1]=(p+1)+1=a[2]

在這裏插入圖片描述

注意(敲黑板,這是重點):

  • 指針變量和數組在訪問數組中元素時,一定條件下其使用方法具有相同的形式,因爲指針變量和數組名都是地址量
  • 但指針變量和數組的指針(或叫數組名)在本質上不同,指針變量是地址變量,而數組的指針是地址常量
p++,p--       //可以使用
a++,a--       //語法錯誤,a是地址常量,不能自加
a+1, *(a+2)   //可以使用

例:int a[]={1,2,3,4,5,6,7,8,9,10}, *p = a,i; 下列a數組元素地址的正確表示是:
(A)&(a+1) (B)a++ (C)&p (D)&p[i]

解析:正確答案D,首先A選項中a本身就是地址,所以a+1即可,不用再取地址,同理的C也一樣,最後a是地址常量,不能自加

四、指針與二維數組

  • 在C語言中,二維數組的元素連續存儲,按行優先存
  • 可把二維數組看作由多個一維數組組成。
比如int a[3][3],含有三個元素:a[0]、a[1]、a[2]
元素a[0]、a[1]、a[2]都是一維數組名
  • 二維數組名代表數組的起始地址,數組名加1,是移動一行元素。因此,二維數組名常被稱爲行地址
int a[3][3]={{1,2,3},{4,5,6},{7,8,9}}; 
int *p;
p = &a;  //好的編譯器會提示報錯,因爲此時兩者的類型是不一樣的,這是爲什麼呢,下面我舉個例子

printf("a:%p a+1:%p\n",a,a+1);
//a:000000000062FDF0   a+1:000000000062FDFC
//執行後發現兩者差了12個字節,即3個int變量
//而由上面所學的一維數組可知,執行p+1後只是加了一個數組元素變量,所以兩者的性質是不同的
  • 存儲行地址的指針變量,叫做行指針變量。形式如下:

<存儲類型> <數據類型> (*<指針變量名>)[表達式] ;
例如,int a[2][3]; int (*p)[3];
方括號中的常量表達式表示指針加1,移動幾個數據。
當用行指針操作二維數組時,表達式一般寫成1行的元素個數,即列數。

int a[3][3]={{1,2,3},{4,5,6},{7,8,9}}; 
int (*p)[3];
p = a;

//比較兩者是否不同 
printf("a:%p a+1:%p\n",a,a+1);
printf("p:%p p+1:%p\n",p,p+1);

//打印a[1][1] 
printf("%d,%d,%d,%d\n",a[1][1],p[1][1],*(*(a+1)+1),*(*(p+1)+1));

由上面打印a[1][1],就可以得出用指針來表示二維數組的方法,這裏我就不一一列舉了,相信大家看看就一定會理解的
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章