C語言中取地址符&做函式形參?—— 引用的討論
取地址符&做函式形參?
C語言強大且危險
引入
這個問題花去了整整一天的研究。
先看一段嚴蔚敏的《資料結構》中棧的例程:
這裡面的&S第一遍看的時候想當然的認為是取了SqStack結構體的S的地址,沒有細想。然後又看到了這句。
- 1
- 2
我開始突然發現,這真的是取地址符嗎,對照了我自己寫的程式,仔細推敲發現不太對。
仔細看這裡的&e,如果這是個整型的棧,那麼SElemType就是int,那麼這裡就等於:
- 1
類比的疑問
我們都知道:
- 1
- 2
那麼難道說是
- 1
仔細看下接下來的函式定義:
顯然這裡可以看出由於top指標指向的是SElemType型別,所以e是SElemType型別的。所以以上類比顯然是不對的。
C/C++中的引用引數
查詢了很多的資料發現,這個實際上是C++裡的形參符號,必須要在跟在資料型別的後面使用。在函式內部對形參的操作都等同於直接操作原變數。
先說形參和實參
學過C語言的都知道,一個經典的例子是關於寫一個交換兩個變數a,b的值的函式:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
我們都知道把a,b作為形參傳入時,會臨時的分配形參空間讀取實參a,b的值存入,這裡的形參a,b實際地址是不同於原來的實參。
繼續準確點說, 在程式執行的時候會分配一個全域性區,我們這裡說的a,b實際上屬於全域性變數,儲存在全域性區,也有的地方叫做靜態區。而這裡的形參儲存在棧區,僅僅是儲存了全域性量的值,所以所有對形參a,b的操作都和靜態區的a,b無關。
這裡實參傳遞給形參的過程叫做值傳遞。
附:C/C++程式的記憶體分配知識
一個由C/C++編譯的程式佔用的記憶體分為以下幾個部分 :
1、棧區(stack)― 由編譯器自動分配釋放 ,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。
2、堆區(heap) ― 一般由程式設計師分配釋放, 若程式設計師不釋放,程式結束時可能由OS回收 。注意它與資料結構中的堆是兩回事,分配方式倒是類似於連結串列。這個空間是公用的,如果沒有釋放會使得可用堆區空間變小,最好在申請後手動釋放。
3、全域性區(靜態區)(static)―,全域性變數和靜態變數的儲存是放在一塊的,初始化的全域性變數和靜態變數在一塊區域, 未初始化的全域性變數和未初始化的靜態變數在相鄰的另一塊區域。 - 程式結束後由系統釋放
4、文字常量區 ―常量字串就是放在這裡的。 程式結束後由系統釋放
5、程式程式碼區―存放函式體的二進位制程式碼。
所以我們可以理解為,這裡的&e是為了說明e變數不是僅僅的把值傳遞進了函式內部。
那怎麼通過函式操作函式外部的引數呢?
根據C語言學習中標準解法,一是將實參的地址傳遞進函式中函式中,通過地址直接操作原變數的值;二是利用函式本身的返回。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
必須先弄清楚各種地址
要理清引用引數的使用和原理,明白這個&符號和指標的區別,先必須搞清楚資料的地址。
在《作業系統》中,可以得知三種地址的區別:邏輯地址、線性地址和實體地址。
關於這三者的區別可以看這裡:
作業系統邏輯地址、線性地址和實體地址
http://www.cnblogs.com/dirichlet/archive/2011/03/18/1987746.html
或者這裡
http://blog.csdn.net/geekwill/article/details/52449855
這裡用圖簡單通俗的說下,為了通俗易懂,嚴格上並不準確:
我們的程式在作業系統中執行的時候,會給我們的程式(程式)在記憶體中分配一些空間。為了方便說明,這裡假設記憶體是16位地址(實際上32位地址支援4G記憶體),我們可以看到a的實體地址是0x23。
然後0x2300是什麼呢,這個是程式資料段的首地址,一般我們習慣叫做程式執行的入口地址。
像上面的圖所示,我們通過&a把a的邏輯地址傳遞進了函式swap中,然後swap函式通過*a找到a的實體地址,這個是作業系統完成的,其中會經過一些過程,需要先變換為線性地址。
那麼我們可以總結:
這裡關於各種變數的記憶體地址相關可以參考:
C語言記憶體地址基礎
http://blog.jobbole.com/44845/
關於C語言的函式呼叫過程更加深度嚴謹(也更難懂)的知識,牆裂推薦這篇文章:
深入理解C語言的函式呼叫過程
http://blog.chinaunix.net/uid-23069658-id-3981406.html
通過引用傳遞和通過指標傳遞?
之前的兩個例子,分別用常規的值傳遞和指標的傳遞實現資料交換的過程看起來不同,其實都是差不多的。實質上都是值傳遞。
第一個例子的執行過程:
第二個例子的執行過程:
可以看出實際上利用指標的方法也只是把a,b的邏輯地址作為一個整數通過值傳遞到形參裡儲存起來了,值傳遞的內容是a,b的邏輯地址。這兩種方式都需要額外的開闢棧區的記憶體,而且指標操作是易錯且不安全的。
下面是通過引用引數完成的交換過程。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
有些文章說道,通過引用的方式傳遞給函式的是變數的地址,這種方式叫做地址傳遞方式,還提到這是和“值傳遞”十分不同的方式。
有些書說道:“引用實際上是取了個‘別名’”
還有的書和文章說道引用是比通過指標傳遞更加高效的方式,因為不需要開闢新的記憶體空間用來拷貝實參的地址。
真的嗎?
引用實現原理的討論
先討論引用實現的系列文章,大佬們講得比較透徹,而且論據豐富。
c++中的引用的使用原理以及使用例項
http://blog.csdn.net/ztz0223/article/details/1639305
C++ 引用 引數傳遞 機制【強烈推薦】
http://blog.csdn.net/huqinweI987/article/details/50769096
C++引用的本質與修改引用的方法
http://blog.csdn.net/huqinweI987/article/details/24874403
舉例剖析C++中引用的本質及引用作函式引數的使用
http://www.jb51.net/article/80911.htm
如果不想看乾貨長文的就看下下面的通俗簡短討論吧。
我們看下下面這段小程式:
- 1
- 2
- 3
- 4
彙編(偽彙編)解析如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
實際上,通過對彙編的分析可以看出:
-
“引用是一個別名”的說法並不準確,實際上實現過程中引用也可以看成是一種指標,實際上引用變數儲存的就是引用物件的地址,也要佔用記憶體空間(和指標佔用大小不同),只不過C++的標準規定了引用初始化完畢之後,對引用的操作就等於是對實際物件的操作。
-
雖然引用可以看做特殊的指標,對引用的操作會被編譯器解釋成對地址指向的目標的操作。但和*p這種取指標指向物件的方式不同,這種方式不會開闢臨時空間儲存指標指向的物件。如果指向物件很大,操作重複數很多,這個差異就會對效能有十分大的影響。
-
引用的本身值,即引用物件的地址不可以像指標變數一樣修改,對引用的操作只會解釋成對引用物件的操作,可以理解引用變數是一個靜態的指標。
對第2條的解釋,關於指標操作拷貝副本和引用節省空間的詳細解釋可以看上面的文章—— C++ 引用 引數傳遞 機制【強烈推薦】。
轉載來自:http://m.blog.csdn.net/JayRoxis/article/details/73060770
相關文章
- C語言中變參函式傳參探究C語言函式
- C 語言中的 time 函式函式
- C語言中的abort函式C語言函式
- PHP取地址符(引用符)&詳解PHP
- C語言中qsort函式的用法C語言函式
- C語言中函式printf()和函式scanf()的用法C語言函式
- C語言中函式的返回值C語言函式
- 淺談C語言中函式的使用C語言函式
- C語言中的*和&符號C語言符號
- 獲取javascript函式形參的數目JavaScript函式
- C語言中的 static變數、static函式C語言變數函式
- 執行緒回撥函式形參不能用引用執行緒函式
- linux下C語言中的mkdir函式與rmdir函式LinuxC語言函式
- 淺析C語言中的setjmp與longjmp函式C語言函式
- 詳細對比C語言中的chmod()函式和fchmod()函式C語言函式
- C語言中的各種修飾符C語言
- C語言中庫函式呼叫幾例(轉)C語言函式
- c語言中const修飾符C語言
- JavaScript 函式形參和實參JavaScript函式
- js獲取指定函式的形參程式碼例項JS函式
- php中函式的實參和形參PHP函式
- c語言中通過函式指標實現函式過載C語言函式指標
- C語言 形參和實參C語言
- C語言中資料型別轉換函式C語言資料型別函式
- C++ 獲取指定的過載函式地址C++函式
- C語言中,&和&&都是做什麼的?C語言
- C 語言中返回字串函式的四種實現方法字串函式
- javascript函式使用length屬性獲取形參的數目JavaScript函式
- day10:函式的實參&形參函式
- 關於c語言中堆、棧認識的彙總(參考論壇回帖)C語言
- C語言中使用系統自帶的快排函式C語言函式
- dart系列之:dart語言中的函式Dart函式
- js 函式中形參與實參的關係JS函式
- 對C語言中無符號型別的建議C語言符號型別
- python關於函式形參Python函式
- 函式形參與實參的體會與對比函式
- C語言中關於時間的函式函式
- 解決vscode c語言中scanf函式的輸入問題VSCodeC語言函式