旁白
最近小漁夫在看嚴蔚敏、李冬梅《數據結構 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++
實現算法的時候再行補上。
以上個人拙作,希望能給路上同仁帶去點光。
若不小心對你有啟發,評論留下你的故事,點贊分享讓更多人為你受益。