天天看點

學生時代所學的一些 C 語言知識點回顧(2)——指針學生時代所學的一些 C 語言知識點回顧(2)——指針

@[toc]

學生時代所學的一些 C 語言知識點回顧(2)——指針

一 前言

承接上一篇,對 C 語言中的指針進行了回顧總結。文中的例子均為本人純手工輸入,在 Linux 環境中全部編譯實踐過。由于平時工作中大部分時間都使用 Linux ,許多深入的 Linux 體系知識需要掌握 C 語言才能深入地了解,故此,對 C 語言進行了一次回顧。工作多年以後,才發現:曾經所學的一切知識并非一無是處,而是那時的自己太粗淺,太急功近利。就像 C 語言,在目前的工作中仍能發揮重大作用,時常能起到事半功倍的效果。關于這些,在後期我的分享中将會完美呈現,敬請關注。

二 一些例子

# include<stdio.h>

int main(int argc,char *argv[]){
  int i = 10, j, *p, *q, *x = &i;
  p = &i;
  q = p;
  j = *&i;
  printf("p = %d\n",*p);
  printf("q = %d\n",*q);
  printf("x = %d\n",*x);
  printf("j = %d\n",j);
  scanf("%d",&i);
  printf("p = %d\n",*p);
  printf("q = %d\n",*q);
  printf("x = %d\n",*x);
  printf("j = %d\n",j);
  return 0;
}
/*
p = 10
q = 10
x = 10
j = 10
98
p = 98
q = 98
x = 98
j = 10
*/           

這個例子種,指針變量 x 在聲明的同時進行了初始化工作,這種操作是合法的。在聲明之後再進行初始化則需要以類似于指針變量 p 的方式進行初始化,仍然使用 *p = &i 這種聲明方式是無法通過編譯的。

通過 *p 這種方式使用指針值叫做間接尋址。此處的 * 稱之為間接尋址運算符。

多個指針變量指向同一變量時,該變量的值改變之後,指針變量的值也随之改變。

*&i 對變量 i 使用 & 運算符産生指向指針變量的指針,對指針使用 * 運算符則可以傳回到原始變量。

# include<stdio.h>

int main(int argc,char *argv[]){
  int *p, *q, i = 989, j = 2019;
  p = &i;
  q = &j;
  q = p;
  printf("p = %d\n",*p);
  printf("p : %p\n",p);
  printf("j = %d\n",j);
  printf("q = %d\n",*q);
  printf("q : %p\n",q);
  return 0;
}
/*
p = 989
p : 0x7fff51751e5c
j = 2019
q = 989
q : 0x7fff51751e5c
*/           

同類型的指針變量可以互相複制,複制之後指向同一變量。

# include<stdio.h>

int main(int argc,char *argv[]){
  int *p, *q, i = 989, j = 2019;
  p = &i;
  q = &j;
  *q = *p;
  printf("p = %d\n",*p);
  printf("p : %p\n",p);
  printf("j = %d\n",j);
  printf("q = %d\n",*q);
  printf("q : %p\n",q);
  return 0;
}
/*
p = 989
p : 0x7ffc00d359ac
j = 989
q = 989
q : 0x7ffc00d359a8
*/           

這段代碼看起來跟上一段十分相似,但是運作結果卻截然不同。指派語句 q = p 把指針變量 p 的值複制到 q 指向的對象中,也就是 j 中,但是 p 和 q 的位址是不一樣的。

# include<stdio.h>

int main(int argc,char *argv[]){
  char i, *p;
  p = &i;
  scanf("%c",p);
  printf("p = %c\n",*p);
  printf("i = %c\n",i);
  return 0;
}
/*
e
p = e
i = e
*/           

在這一段代碼中,如果删除 “p = &i;” 這一句,那麼這段代碼是不能順利通過編譯的,函數 scanf() 中的 變量 p 就相當于 &i, scanf() 讀入的字元并存儲于 i 中。此時,scanf() 中不能再使用運算符 &。

# include<stdio.h>

int main(int argc,char *argv[]){
  int a, b;
  int *max(int *,int *);
  scanf("%d%d",&a,&b);
  printf("The max number is: %d\n",*max(&a,&b));
  return 0;
}

int *max(int *a, int *b){
  *a = *a + 5;
  if (*a > *b)
    return a;
  else
    return b;
}
/*
1 4
The max number is: 6
*/           

指針變量作為形參進行傳遞、運算,并函數的傳回值類型為指針。再繼續看下一段代碼:

# include<stdio.h>

int main(int argc,char *argv[]){
  int a, b;
  int max(int *,int *);
  scanf("%d%d",&a,&b);
  printf("The max number is: %d\n",max(&a,&b));
  return 0;
}

int max(int *a, int *b){
  *a = *a + 5;
  if (*a > *b)
    return *a;
  else
    return *b;
}
/*
1 4
The max number is: 6
*/           

同樣的輸入,一樣的輸出結果,形式不一樣,實際上實作原理也是一樣的,都傳回指針,可以類比來了解。

# include<stdio.h>

int main(int argc,char *argv[]){
  int x = 2019, y=2023;
  int *a = &x, *b = &y, c;
  c = (*a + *b) * 2 + 20;
  printf("Sum: %d\n",c);
  return 0;
}
// Sum: 8104           

以上代碼的第4行和第5行互換,将不能通過編譯。C 語言嚴格遵循先聲明後使用的原則,指針也不例外。間接尋址在表達式中是可以直接使用的。需要說明的是:“int *a = &x, *b = &y, c;”這一行中的 * 不是間接尋址運算符,其作用是告知編譯器 a 和 b 是兩個指向 int 類型變量的指針。 “c = (*a + *b) * 2 + 20;”這一行的前兩個 * 是間接尋址運算符,第三個 * 是乘法運算符。

# include<stdio.h>

int main(int argc,char *argv[]){
  int p;
  int example(int *);
  scanf("%d",&p);
  printf("%d\n",example(&p));
  return 0;
}

int example(int *p){
  int a = 10086;
  printf("The original value is: %d\n",*p);
  p = &a;
  return *p;
}           
# include<stdio.h>

int main(int argc,char *argv[]){
  int p;
  int example(int *);
  scanf("%d",&p);
  printf("%d\n",example(&p));
  return 0;
}

int example(int *p){
  printf("The original value is: %d\n",*p);
  *p = 10086;
  return *p;
}           
# include<stdio.h>

int main(int argc,char *argv[]){
  int p;
  int example(const int *);
  scanf("%d",&p);
  printf("%d\n",example(&p));
  return 0;
}

int example(const int *p){
  int x = 10086;
  printf("The original value is: %d\n",*p);
  p = &x;
  return *p;
}
/*
98
The original value is: 98
10086
*/           

以上三段代碼編譯執行後均能得到一緻的結果,但是如下代碼卻不能通過編譯:

# include<stdio.h>

int main(int argc,char *argv[]){
  int p;
  int example(const int *);
  scanf("%d",&p);
  printf("%d\n",example(&p));
  return 0;
}

int example(const int *p){
  printf("The original value is: %d\n",*p);
  *p = 10086;
  return *p;
}           

這說明使用了 const 關鍵字之後,不能改變指針指向的整數,但是能改變指針自身。因為實參是按值進行傳遞的,是以通過指針指向其他地方的方法給 p 賦新值不會對函數外部産生任何影響。在聲明時,關鍵字 const 是不能省略的。繼續看下面的代碼。

# include<stdio.h>

int main(int argc,char *argv[]){
  int p;
  int example(int * const);
  scanf("%d",&p);
  printf("%d\n",example(&p));
  return 0;
}

int example(int * const p){
  printf("The original value is: %d\n",*p);
  *p = 10086;
  // int x = 10086;
  // p = &x;
  return *p;
}
/*
98
The original value is: 98
10086
*/           

本段代碼中,如果取消注釋部分,則不能通過編譯。在此處,可以改變指針指向的整數,但是不能改變改變指針自身。進一步嘗試:

# include<stdio.h>

int main(int argc,char *argv[]){
  int p;
  int example(const int * const);
  scanf("%d",&p);
  printf("%d\n",example(&p));
  return 0;
}

int example(const int * const p){
  printf("The original value is: %d\n",*p);
  // *p = 10086;
  // int x = 10086;
  // p = &x;
  return *p;
}
/*
98
The original value is: 98
98
*/           

本段代碼中出現了兩個 const 關鍵字。代碼中被注釋的3行,取消任意一部分均不能通過編譯。這種情況說明:通過這種聲明之後,既不能改變指針指向的整數,也不能改變指針自身。不過這種情況比較少見。

三 小結

此部分主要回顧了指針,使用了較長篇幅驗證了 const 關鍵字,可能還存在較多了解上的誤區,希望各位多多指點、溝通交流,彼此進步。

繼續閱讀