旁白
最近小漁夫在看嚴蔚敏、李冬梅《資料結構 c語言版》(第2版),學到第二章順序表的實現時,看到函式引數一會是SqList &L
、一會又是SqList L
、一會ElemType &e
、一會又ElemType e
,當場大寫的黑人問號加感嘆號。這都什麼玩意,一會有&
一會又沒有,都代表什麼意思呢?
於是帶著這些問號去找答案,上網上看了很多,看到的比較零散,於是我整理了一下,理清原因後,心想估計也有同學跟我一樣的黑人問號,於是就有著這篇文章,希望能有點幫助吧。
先說答案
嚴書裡的程式碼是虛擬碼,什麼是虛擬碼?顧名思義不是真的程式碼,拿到電腦上去跑不起來的程式碼,虛擬碼重點是表達思路、表達想法的。
所以,書裡函式引數中有&
,想表達的意思是:希望通過函式改變該引數的值。可以看到書裡在新建順序表、插入一個資料、刪除資料等改變順序表的操作時用到了SqList &L
,因為這些操作會改變表的內容;在查詢操作時,沒有改變表,順序表前沒有&
,用到了SqList L
;在取值時用到了ElemType &e
,是想把取到的值通過引數返回。
在C
語言裡只能通過指標實現。書裡寫的是虛擬碼,而不是真正的C
程式碼;在C++
裡可以通過指標和引用(C
語言無引用)實現。
有些同學可能不太理解怎麼通過函式改變傳進來引數的值,下面詳細介紹一個例子就明白了。
詳細介紹
例子是用c
語言的指標實現的,說起指標不得不提兩個符號*
與&
,那先來看指標中的*
與&
。
C語言指標中的*
與&
首先,一般看一個變數,看它的3點。變數名稱、資料、地址。如定義一個變數int a = 5
,變數名是a
,值是5
,在記憶體中的地址是0x00001111
。
瞭解了變數,就可以瞭解指標了,指標可以理解成記憶體裡的地址,指標變數就是儲存地址的變數。
&
是取地址運算子,用於取變數地址;例如:int a = 5
, &a
表示變數a
的地址0x00001111
。
*
出現在不同的地方含義不同,但就我現在理解的,一般出現在兩個地方:
- 函式引數中和變數定義中,表示定義一個指標變數。如
int *p ,int *p = &a.
- 等號右邊,
*(地址)
表示取值運算。如int a = 5; int *p = &a; *p
,中變數啊的值為5
,指標變數p
的值為0x00001111
,*p
的值為5
。
例子:交換兩變數的值
瞭解了指標後,來看下這個例子。例子中寫了兩個函式,來交換兩變數的值,第一個函式swap1
沒用指標,第二個函式swap2
用了指標。結果你猜,哪個函式能改變傳進來引數的值?
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
int num1 = 1;
int num2 = 2;
swap1(num1, num2);
printf("num1 = %d, num2 = %d\n", num1, num2);
swap2(&num1, &num2);
printf("num1 = %d, num2 = %d\n", num1, num2);
// test();
return 0;
}
void swap1(int a, int b) {
int temp;
temp = a;
a = b;
b = temp;
}
void swap2(int *a, int *b) {
int temp;
temp = *a;
*a = *b;
*b = temp;
}
如圖:執行一下,得到結果。第二個函式起作用了,也就是用了指標的函式能改變傳進來引數的值,實現交換兩變數的效果。這就是通過函式改變傳進來引數的值。
到這裡,已經得到答案了。
至於,為什麼第一個函式沒有實現交換,有興趣的再往下看看。
單步除錯一下,我發現了其中的奧妙。
除錯開始,如下圖,變數num1
值為1
,地址為0x62fe1c
,變數num2
值為2
,地址為0x62fe18
。
往下走,開始呼叫函式,進到函式swap1
裡。如圖,變數num1
和num2
沒什麼變化。引數a
和b
已經接收到傳進來的引數了。同時發現num1
、num2
的 地址和a
、b
不一樣,原來函式會把形式引數當作區域性變數,然後在棧中開闢記憶體空間,用於存放由主調函式傳遞進來的實參值,從而形成了實參的一個副本(替身)。
再往下走,走完發現了驚喜,與上圖相比a
和b
互換了。而變數num1
和num2
和還是沒什麼變化。
最後,函式swap1
執行結束,輸出結果num1 = 1, num2 = 2
。可以發現雖然變數交換了,但是隻是交換了副本(替身)。至於為什麼用了指標就可以交換真身呢?感興趣,動動小手去探索探索吧,哈哈哈。
附錄
1. *p和**p
的區別
int *p
是一級指標,存放的是一個變數的地址。
int **p
是二級指標,存放的是指標變數的地址。
例子:
//定義整形變數
int a = 6;
//定義一個指標指向這個變數
int *p = &a;
//定義一個二級指標指向p指標
int **pp = &p;
// 那麼取出6的方式都有哪些呢?
printf("a=%d", a);
printf("a=%d", *p);
printf("a=%d", **pp);
以上3行輸出的值都是6 。
2. *&p和&*p
的區別
根據單目運算子運算的優先順序,*&p
等價於*(&p)
。&*p
等價於&(*p)
。
-
如果
p
是指標變數,那麼*&p = p
,&*p = p
,都等於p
,但還沒定義p
指向哪,存的是誰的地址。 -
如果
p
是一個int
變數,那麼*&p = p
;而&*p
是非法的,因為*p
非法。
比如int p =10;
那麼*&p = *(&p) = p = 10
(即從p的地址取值),而&*p = &(*p)
則非法,因為p=10,*10
是取記憶體地址為10
的值,這在c語言中是不合法的。
後續
我在學習中發現,c
只能通過指標實現這種方式,而C++
不僅能通過指標實現,還能通過引用實現。並且C++
還提倡使用引用。本來想繼續寫寫,什麼是C++
引用,為什麼C++
引進這個概念,這個概念有什麼好處、C++
指標引數和引用引數有什麼區別的。但發現跟主題好像離得有點遠,扯到C++
去了,作罷。看看後面有機會用C++
實現演算法的時候再行補上。
才疏學淺,如有不當,請批評指正。
你的支援也是我的動力,最後筆記對你有用,別忘了點贊支援下哦。