C語言基礎-指針高級

指針高級

數組指針(也稱行指針)

定義:

int (*p)[n];

()優先級高,首先說明p是一個指針,指向一個整型的一維數組,這個一維數組的長度是n,也可以說是p的步長。也就是說執行p+1時,p要跨過n個整型數據的長度。

如要將二維數組賦給一指針,應這樣賦值:

int a[3][4];
int (*p)[4]; //該語句是定義一個數組指針,指向含4個元素的一維數組。
 p=a;       //將該二維數組的首地址賦給p,也就是a[0]或&a[0][0]
 p++;      //該語句執行過後,也就是p=p+1;p跨過行a[0][]指向了行a[1][]

所以數組指針也稱指向一維數組的指針,亦稱行指針。

指針數組

定義:

int *p[n];
  • []優先級高,先與p結合成爲一個數組,再由int*說明這是一個整型指針數組,它有n個指針類型的數組元素。
  • 這裏執行p+1時,則p指向下一個數組元素,這樣賦值是錯誤的:p=a;
  • 因爲p是個不可知的表示,只存在p[0]、p[1]、p[2]…p[n-1],而且它們分別是指針變量可以用來存放變量地址。但可以這樣 p=a; 這裏p表示指針數組第一個元素的值,a的首地址的值。

二級指針

  • 指針可以指向一份普通類型的數據,例如 int、double、char 等,也可以指向一份指針類型的數據,例如 int *、double
    *、char * 等。
  • 如果一個指針指向的是另外一個指針,我們就稱它爲二級指針,或者指向指針的指針。

假設有一個 int 類型的變量 a,p1是指向 a 的指針變量,p2 又是指向 p1 的指針變量,它們的關係如下圖所示:
在這裏插入圖片描述
將這種關係轉換爲C語言代碼:

int a =100;
int *p1 = &a;
int **p2 = &p1;
int a=100;
int *p1 =&a;
int **p2= &p1;

獲取指針指向的數據時,一級指針加一個*,二級指針加兩個*,三級指針加三個*,以此類推,請看代碼:

#include <stdio.h>
int main()
{
 int a =100;
 int *p1 = &a;
 int **p2 = &p1;
 int ***p3 = &p2;
 printf("%d, %d,%d, %d\n", a, *p1, **p2, ***p3);
 printf("&p2  = %#X, p3 = %#X\n", &p2, p3);
 printf("&p1 = %#X, p2 = %#X, *p3 = %#X\n", &p1, p2, *p3);
 printf(" &a= %#X, p1 = %#X, *p2 = %#X, **p3 = %#X\n", &a, p1, *p2, **p3);
 return 0;
 }

運行結果:

100, 100, 100, 100

&p2 = 0X28FF3C, p3 = 0X28FF3C

&p1 = 0X28FF40, p2 = 0X28FF40, *p3 =0X28FF40

&a = 0X28FF44, p1 = 0X28FF44, *p2 =0X28FF44, **p3 = 0X28FF44

以三級指針 p3 爲例來分析上面的代碼。
*p3等價於((p3))。p3得到的是 p2 的值,也即 p1 的地址;(p3) 得到的是 p1 的值,也即a 的地址;經過三次“取值”操作後,((*p3)) 得到的纔是 a 的值。
假設 a、p1、p2、p3 的地址分別是 0X00A0、0X1000、0X2000、0X3000,它們之間的關係可以用下圖來描述:
在這裏插入圖片描述
【實例】使用指針遍歷二維數組。

#include   <stdio.h>
int  main()
{
 int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11};
 int(*p)[4];
 int i,j;
 p=a;
 for(i=0; i<3; i++)
 {
     for(j=0; j<4; j++)
 		printf("%2d ",*(*(p+i)+j));
     printf("\n");
 }
	 return 0;
  }

運行結果:

 0  1   2   3

 4  5   6   7

 8   9 10  11

指針數組和二維數組指針的區別

指針數組和二維數組指針在定義時非常相似,只是括號的位置不同:

 int *(p1[5]);  //指針數組,可以去掉括號直接寫作 int *p1[5];
 int (*p2)[5];  //二維數組指針,不能去掉括號

指針數組和二維數組指針有着本質上的區別:

  • 指針數組是一個數組,只是每個元素保存的都是指針,以上面的 p1 爲例,在32位環境下它佔用 4×5 = 20 個字節的內存。二維數組指針是一個指針,它指向一個二維數組,以上面的 p2 爲例,它佔用 4 個字節的內存。

結構體指針

程序在運行過程中需要的是數據和指令的地址,變量名、函數名、字符串名和數組名在本質上是一樣的,它們都是地址的助記符:在編寫代碼的過程中,我們認爲變量名錶示的是數據本身,而函數名、字符串名和數組名錶示的是代碼塊或數據塊的指針也可以指向一個結構體,定義的形式一般爲:

struct 結構體名 *變量名;

注意:

  • 結構體變量名和數組名不同,數組名在表達式中會被轉換爲數組指針,而結構體變量名不會,無論在任何表達式中它表示的都是整個集合本身,要想取得結構體變量的地址,必須在前面加&,所以給pstu 賦值只能寫作:

     struct stu *pstu = &stu1;
    

而不能寫作:

	struct stu *pstu = stu1;

【示例】結構體指針的使用。

#include <stdio.h>
struct stu
{  
char *name;  //姓名 
int num;  //學號 
int age;  //年齡
char group;  //所在小組
float score;  //成績
};
int main()
{
struct stu stu1 = { "Tom", 12, 18, 'A', 136.5 }, *pstu = &stu1;
//讀取結構體成員的值
printf("%s的學號是%d,年齡是%d,在%c組,今年的成績是%.1f!\n", (*pstu).name, (*pstu).num, (*pstu).age, (*pstu).group,(*pstu).score);
printf("%s的學號是%d,年齡是%d,在%c組,今年的成績是%.1f!\n", pstu->name, pstu->num, pstu->age, pstu->group, pstu->score);
return 0;

}

運行結果:

Tom的學號是12,年齡是18,在A組,今年的成績是136.5!
Tom的學號是12,年齡是18,在A組,今年的成績是136.5!

結構體變量名代表的是整個集合本身,作爲函數參數時傳遞的整個集合,也就是所有成員,而不是像數組一樣被編譯器轉換成一個指針。如果結構體成員較多,尤其是成員爲數組時,傳送的時間和空間開銷會很大,影響程序的運行效率。所以最好的辦法就是使用結構體指針,這時由實參傳向形參的只是一個地址,非常快速。

【示例】計算全班學生的總成績、平均成績和以及 140 分以下的人數。

#include <stdio.h>
struct stu
{	    
 char *name;  //姓名
int num;  //學號         
int age;  //年齡          
char group;  //所在小組    
float score;  //成績    
};
struct stu stus[] = {
{"Li ping", 5, 18, 'C', 145.0},
{"Zhang ping", 4, 19, 'A', 130.5},
{"He fang", 1, 18, 'A', 148.5}, 
{"Cheng ling", 2, 17, 'F', 139.0},
{"Wang ming", 3, 17, 'B', 144.5}
};
 void average(struct stu *ps, int len);
 int main()
 {
  int len = sizeof(stus) / sizeof(struct stu);
  average(stus, len);
  return 0;  
}
void average(struct stu *ps, int len)
{
int i, num_140 = 0;
float average, sum = 0;
for(i=0; i<len; i++)
{  
 sum += (ps + i) -> score;    
if((ps + i)->score < 140) 
	num_140++;
  }
printf("sum=%.2f\naverage=%.2f\nnum_140=%d\n", sum, sum/5,num_140);

}

運行結果:

sum=707.50

average=141.50

num_140=2首地址;程序被編譯和鏈接後,這些名字都會消失,取而代之的是它們對應的地址。

無類型指針

無類型指針指的是void *這種指針,表示可以指向任何數據類型。

比如

int n = 3;

int *p = NULL;  //說明指針p此時空閒,沒有指向任何有意義的內存空間

void *gp = &n;  //無類型指針gp指向整型變量n

p = (int *)gp;   //把無類型指針轉換爲整型指針

printf("%d\n", *p);

結果輸出3,說明無類型指針可以轉換成任何數據類型的指針。

常量指針 和 指針常量
我們先總結一下 常量指針 和 指針常量 的區別

  1. (指針)和 const(常量)誰在前先讀誰 ;

  2. *象徵着地址,const象徵着內容;

  3. 誰在前面誰就不允許改變。

    int a =3;  
    
    int b = 1;  
    
    int c = 2;  
    
    int const *p1 = &b;//const 在前,定義爲常量指針  
    
    int *const p2 = &c;//*在前,定義爲指針常量   
    

常量指針p1:指向的地址可以變,但內容不可以重新賦值,內容的改變只能通過修改地址指向後變換。

p1 = &a是正確的,但 *p1 = a是錯誤的。

指針常量p2:指向的地址不可以重新賦值,但內容可以改變,必須初始化,地址跟隨一生。

p2= &a是錯誤的,而*p2 = a 是正確的。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章