真正搞懂傳值引用和傳指標引用
通過一個基本原則和兩個例子講述傳值引用和傳指標引用的區別,並且從系統底層解析這兩種呼叫的區別
- acmore
- 2017.11.14
1. 問題概述
在C語言呼叫函式時,有兩種傳引數的方法,一種是傳值,另一種是傳引數。對於C語言的初學者(甚至是一些C語言的熟練使用者)而言,區別這兩種形式往往比較困難,再加上C語言有指標,以及指標的指標等等,複雜多變的形式會使得這個過程更加困難。
很多初學者會嘗試通過形式來記憶這兩種方式,比如函式宣告中有*
的就是傳指標,沒有的就是傳值,但是如果傳入的引數本身就是一個指標,無論是傳值還是傳指標,函式宣告都會有*
,這種時候原有的形式記憶不再有效,需要再次記憶另外一套形式。形式是無窮無盡的,只有明白指標型別的本質,以及在函式呼叫過程中系統裡到底發生了什麼,才能真正理解這兩種方式。
2. 簡單認識
現在我們有兩個函式,作用都是修改一個int
型別的值,分別如下:
#include <stdio.h>
void modify1(int a) {
a = 10086;
}
void modify2(int *a) {
*a = 10086;
}
int main() {
int x = 1;
printf("%d\n", x);
modify1(x);
printf("%d\n", x);
int y = 1;
printf("%d\n", y);
modify2(&y);
printf ("%d\n", y);
return 0;
}
執行程式,會輸出什麼呢?
1
1
1
10086
這個程式很簡單,我們可以輕易看出modify1
是傳值引用,modify2
是傳指標引用。傳值引用不會改變呼叫者的值,而傳指標會改變呼叫者的值,並且在形式上,函式宣告中有*
,函式呼叫有&
。因此我們這樣記憶:只要改變了呼叫者的值,並且有*
的函式就是傳指標引用。
真的是這樣嗎?
3. 深入理解
下面我們定義一個結構體,這個結構體含有兩個整數值,然後類似地寫出兩個修改函式:
#include <stdio.h>
#include <malloc.h>
typedef struct Value {
int a;
int b;
}Value;
void modify1(Value *v) {
v->a = 10086;
v->b = 10010;
}
void modify2(Value **v) {
(*v)->a = 10086;
(*v)->b = 10010;
}
int main() {
Value *v1 = (Value *) malloc(sizeof(Value));
v1->a = v1->b = 1;
printf("%d\t%d\n", v1->a, v1->b);
modify1(v1);
printf("%d\t%d\n", v1->a, v1->b);
Value *v2 = (Value *) malloc(sizeof(Value));
v2->a = v2->b = 1;
printf("%d\t%d\n", v2->a, v2->b);
modify2(&v2);
printf("%d\t%d\n", v2->a, v2->b);
return 0;
}
輸出結果如下:
1 1
10086 10010
1 1
10086 10010
這次我們再按照之前的經驗去判斷,好像有點行不通,因為在對modify1
和modify2
的呼叫上雖然像是一個傳值一個傳指標,但是兩個函式的宣告中都有*
,並且v1
和v2
的值都被改變了,經驗不再適用!
這種時候我們應該回歸底層,老老實實從系統層面理解在程式執行過程中到底發生了什麼,我們首先應該記住的一個最重要的原則是:
- 指標型別和基本型別一樣,本質沒有什麼區別
我們拿型別int
和Value *
做比較,根據上述原則,便有以下幾條原則:
int
在記憶體中佔有一塊空間,那麼Value *
同樣會在記憶體中佔有一塊空間可以得到一個指標指向
int
,那麼也可以有一個指標指向Value *
使用
&
可以取得int
的指標,那麼同樣可以使用&
取得Value *
的指標傳值引用時,有變數
int a
和Value* v
,下邊兩種寫法都是傳值引用- 操作
int
時,函式的引數型別就是int
,呼叫函式時傳入a
- 同理在操作
Value *
時,函式的引數型別就是Value *
,呼叫函式時傳入v
- 操作
- 傳指標引用時,下邊兩種寫法都是傳指標引用
- 操作
int
時,函式的引數型別是int *
,呼叫函式時傳入&a
- 操作
Value *
時,函式的引數型別是Value **
,呼叫函式時傳入&v
- 操作
根據以上原則,不要把指標當做一個特殊的型別,把指標型別那一堆當成一個整體,傳值時直接丟進去,傳指標時就在後邊加一個*
,在對應的呼叫前加上&
即可。
4. 問題拓展
現在我們要完成一個這樣的函式:它可以代替malloc
函式來對我們剛剛宣告的一個指標進行賦值,在第三部分中我們瞭解到,在操作指標型別時,傳值引用一樣可以改變呼叫者的值,所以我們寫出了下邊的程式:
#include <stdio.h>
#include <malloc.h>
typedef struct Value {
int a;
int b;
}Value;
void create(Value *v) {
v = (Value *) malloc(sizeof(Value));
v->a = 10086;
v->b = 10010;
}
int main() {
Value *v1;
create(v1);
printf("%d\t%d", v1->a, v1->b);
return 0;
}
結果輸出如下:
-16219251 -970326017
並沒有符合我們的期待!說好的操作指標就可以高枕無憂了呢?這個時候我們需要從系統底層瞭解傳值和傳指標時到底發生了什麼,如下圖所示
首先v1
的值是create
函式時,就是圖上的①過程,這個時候v1
的值被拷貝到v
中,這時候如果直接對v
操作是沒有問題的,可以改變v1
的值,因為它們倆指向的是同一塊記憶體空間(事實上此時還沒有指向任何有意義的空間),但是在隨後的賦值語句中,即②過程,v
的值立刻被替換為v
進行的操作其實是對v
,也就是
#include <stdio.h>
#include <malloc.h>
typedef struct Value {
int a;
int b;
}Value;
void create(Value **v) {
(*v) = (Value *) malloc(sizeof(Value));
(*v)->a = 10086;
(*v)->b = 10010;
}
int main() {
Value *v1;
create(&v1);
printf("%d\t%d", v1->a, v1->b);
return 0;
}
5. 最後總結
- 指標沒有什麼特殊的,基本資料型別怎麼對待,指標就怎麼對待
- 要理解函式呼叫過程中程序地址空間中的變化,它能直觀地體現傳值引用和傳指標引用的區別,並解決在使用過程中出現的問題
- 不要死記硬背形式,任何問題嘗試迴歸底層理解
相關推薦
Java中的形參和實參的區別以及傳值呼叫和傳引用呼叫
原文地址:http://blog.csdn.net/miniminiyu/article/details/52061401 名詞解析: 1.形參:用來接收呼叫該方法時傳遞的引數。只有在被呼叫的時候才分配記憶體空間,一旦呼叫結束,就釋放記憶體空間。因此僅僅在方法內有效
C語言中交換兩個整數的值之傳值呼叫和傳址呼叫
在C語言中,一說到交換兩個整數的值,大家第一反應可能是這樣的程式碼。定義一個第三方變數來輔助交換。 #include<stdio.h> int main() { int n
scala 傳值引數和傳名引數 ():=>和:=>
傳值引數程式碼示例:def test1(code: ()=>Unit){ println("start") code() //要想呼叫傳入的程式碼塊,必須寫成code(),否則不會呼叫。 println("end") } test1
真正搞懂傳值引用和傳指標引用
通過一個基本原則和兩個例子講述傳值引用和傳指標引用的區別,並且從系統底層解析這兩種呼叫的區別 acmore 2017.11.14 1. 問題概述 在C語言呼叫函式時,有兩種傳引數的方法,一種是傳值,另一種是傳引數。對於C語言的初學者(甚至是
C++傳引用和傳值,傳指標
我們需要傳資料而不改變資料儲存,直接傳值,如int a; 我們需要傳資料,並且改變值大小,需要傳地址,如 int * pa; 我們需要傳資料,並改變數結構中指標的指向,需要傳二級指標,如連結串列中的 node * * l; ... 以前是passl-by-val
從彙編和高階語言的角度理解傳值方式,傳值,傳引用,傳指標的本質機制與區別。白話通俗易懂。
函式的傳參與返回值的方式有傳值和傳遞引用,c語言中就是傳值,而c++擴充套件傳引用。 而傳值分為傳遞值(實參的值,此時形參是實參在記憶體中的一份拷貝,形參在使用時分配記憶體,結束時釋放,實參和形參在記憶體中的地址不同,因此對形參的改變不會改變實參) 傳值的另外一種是傳指標
PHP值傳遞和引用傳遞的區別。什麼時候傳值什麼時候傳引用
(1)按值傳遞:函式範圍內對值的任何改變在函式外部都會被忽略 (2)按引用傳遞:函式範圍內對值的任何改變在函式外部也能反映出這些修改 (3)優缺點: A:按值傳遞時,php必須複製值。特別是對於大型的字串和物件來說,這將會是一個代價很大的操作。 B.按引用傳遞則
C++中傳值引數和引用引數和指標怎樣區別?
C++中傳值引數和引用引數怎樣區別呢? 看以下例子:#include<iostream>using namespace std;void swap(int a,int b){int temp;temp=a;a=b;b=temp;}main(){int a=3,b=
傳值引用和調用引用的區別
生成 ima chang img 代碼 src oid inf clas 只需要記住一句話: 傳值引用一般就是生成一個臨時對象,而引用調用是調用參數本身。 參照下面C語言代碼理解: 在 test.h文件裏實現兩個方法 #include <stdio.
Core Mvc傳值Request和HttpContext
ati quest bsp 需要 tco 修改 onf ice 註入 1.傳值方法 使用Request的方法(1-3): 1)Query:獲取鏈接?後面的值 如:http://localhost:55842/Home/About?name=kxy publ
Ajax傳值以及接受傳值,@ResPonseBody 和 @RequestBody
Ajax對於Java程式設計人員開說可是很重要的,可以說是必會的。 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Title</title&
Python 函式中,引數是傳值,還是傳引用?
在 C/C++ 中,傳值和傳引用是函式引數傳遞的兩種方式,在Python中引數是如何傳遞的?回答這個問題前,不如先來看兩段程式碼。 程式碼段1: def foo(arg): arg = 2 print(arg) a = 1 foo(a) # 輸出:2 print(a) #
淺析scala傳名呼叫和傳值呼叫,: => 與() : =>
函式呼叫一般傳值呼叫,但是,在某些情況下,我們不希望函式的值首先被計算,而是等到呼叫的時候再來進行計算,為了適應這種情景,scala提供了傳名呼叫。 先來看兩個例子: package test /** * Created by layne on
Java中方法呼叫引數傳遞的方式是傳值,儘管傳的是引用的值而不是物件的值。(Does Java pass by reference or pass by value?)
原文地址:http://www.javaworld.com/javaworld/javaqa/2000-05/03-qa-0526-pass.html 在Java中,所有的物件變數都是引用,Java通過引用來管理物件。然而在給方法傳參時,Java並沒有使用傳引用的方式,而是
HTTP傳值方式和請求方式/angular的請求方式
一、Http傳值方式有2種:get、post 二、標準Http協議支援六種請求方法,即:get 、head、put、delete、post、options(通常情況下我們只用到了get和post的請求方式) 三、Http使用get請求介面的時候,一般直接ge
傳指針和傳指針引用的區別/指針和引用的區別(本質)
返回 這樣的 尋址 討論 public 初始 max() 局部變量 構造函數 轉自:http://blog.sina.com.cn/s/blog_673ef8130100imsp.html 指針傳遞參數本質上是值傳遞的方式,它所傳遞的是一個地址值。值傳遞過程中,
徹底搞懂反斜杠“”和正斜杠"/"的區別
影響 使用 web應用 圖片 命令 mic ont http () 正斜杠,符號是"/";反斜杠,符號是"\"。 在知乎中看到一個答案如下: 知乎用戶:“在絕大多數地方,用的都是/(slash),包括Mac/Linux,也包括URL。你唯一需要記住的是,Microsoft這
真正“搞”懂http協議01—背景故事
客戶端 http1.0 開車 溝通 生涯 net erl 慢慢 維基 去年讀了《圖解HTTP》、《圖解TCP/IP》以及《圖解網絡硬件》但是讀了之後並沒有什麽深刻的印象,只是有了一層模糊的脈絡,剛好最近又接觸了一些有關http的相關內容。所以,就打算把它寫成一個系列,一
這一次,真正搞懂信用評分模型(上篇)
工程師 集中 重要 sklearn app 目的 概率 單變量 是我 python風控評分卡建模和風控常識 https://study.163.com/course/introduction.htm?courseId=1005214003&utm_campaign
元件傳值之父傳子、子傳父
父元件傳值給子元件 父元件 <template> <div id="app"> <h1>props使用方式</h1> <hello txt='元件txt' v-bind:ddd="btn