欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文助你搞懂參數(shù)傳遞原理解析(java、go、python、c++)

 更新時間:2021年01月26日 10:50:50   作者:crossoverJie  
這篇文章主要介紹了多種語言參數(shù)傳遞原理解析(java、go、python、c++),本文通過實例圖文相結(jié)合給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

前言

最近一年多的時間陸續(xù)接觸了一些對我來說陌生的語言,主要就是 PythonGo,期間為了快速實現(xiàn)需求只是依葫蘆畫瓢的擼代碼;并沒有深究一些細(xì)節(jié)與原理。

就拿參數(shù)傳遞一事來說各個語言的實現(xiàn)細(xì)節(jié)各不相同,但又有類似之處;在許多新手入門時容易搞不清楚,導(dǎo)致犯一些低級錯誤。

Java

基本類型傳遞

先拿我最熟悉的 Java 來說,我相信應(yīng)該沒人會寫這樣的代碼:

	@Test
 public void testBasic() {
 int a = 10;
 modifyBasic(a);
 System.out.println(String.format("最終結(jié)果 main a==%s", a));
 }

 private void modifyBasic(int aa) {
 System.out.println(String.format("修改之前 aa==%s", aa));
 aa = 20;
 System.out.println(String.format("修改之后 aa==%s", aa));
 }

輸出結(jié)果:

修改之前 aa==10
修改之后 aa==20
最終結(jié)果 main a==10

不過從這段代碼的目的來看應(yīng)該是想要修改 a 的值,從直覺上來說如果修改成功也是能理解的。

至于結(jié)果與預(yù)期不符合的根本原因是理解錯了參數(shù)的值傳遞與引用傳遞。

在這之前還是先明確下值傳遞與引用傳遞的區(qū)別:

這里咱們先拋出結(jié)論,Java 采用的是值傳遞;這樣也能解釋為什么上文的例子沒有成功修改原始數(shù)據(jù)。

參考下圖更好理解:

當(dāng)發(fā)生函數(shù)調(diào)用的時候 a 將自己傳入到 modifyBasic 方法中,同時將自己的值復(fù)制了一份并賦值給了一個新變量 aa 從圖中可以看出這是 aaa 兩個變量沒有一毛錢關(guān)系,所以對 aa 的修改并不會影響到 a。

有點類似于我把蘋果給了老婆,她把蘋果削好了;但我手里這顆并沒有變化,因為她只是從餐盤里拿了一顆一模一樣的蘋果削好了。

如果我想要她那顆,只能讓她把削好的蘋果給我;也就類似于使用方法的返回值。

a = modifyBasic(a);

引用類型傳遞

下面來看看引用類型的傳遞:

 private class Car{
 private String name;

 public Car(String name) {
 this.name = name;
 }

 @Override
 public String toString() {
 return "Car{" +
 "name='" + name + '\'' +
 '}';
 }
 }

		@Test
 public void test01(){
 Car car1 = new Car("benz");
 modifyCar1(car1);
 System.out.println(String.format("最終結(jié)果 main car1==%s", car1));
 }

 private void modifyCar1(Car car){
 System.out.println(String.format("修改之前 car==%s", car));
 car.name = "bwm";
 System.out.println(String.format("修改之后 car==%s", car));
 }

在這個例子里先創(chuàng)建了一個 benzcar1,通過一個方法修改為 bmw 那最開始的 car1 會受到影響嘛?

修改之前 car==Car{name='benz'}
修改之后 car==Car{name='bwm'}
最終結(jié)果 main car1==Car{name='bwm'}

結(jié)果可能會與部分人預(yù)期相反,這樣的修改卻是可以影響到原有數(shù)據(jù)的?這豈不是和值傳遞不符,看樣子這是引用傳遞吧?

別急,通過下圖分析后大家就能明白:

test01 方法中我們創(chuàng)建了一個 car1 的對象,該對象存放于堆內(nèi)存中,假設(shè)內(nèi)存地址為 0x1102 ,于是 car1 這個變量便應(yīng)用了這塊內(nèi)存地址。

當(dāng)我們調(diào)用 modifyCar1 這個方法的時候會在該方法棧中創(chuàng)建一個變量 car ,接下來重點到了:

這個 car 變量是由原本的入?yún)?car1 復(fù)制而來,所以它所對應(yīng)的堆內(nèi)存依然是 0x1102;

所以當(dāng)我們通過 car 這個變量修改了數(shù)據(jù)后,本質(zhì)上修改的是同一塊堆內(nèi)存中的數(shù)據(jù)。從而原本引用了這塊內(nèi)存地址的 car1 也能查看到對應(yīng)的變化。

這里理解起來可能會比較繞,但我們記住一點就行:

傳遞引用類型的數(shù)據(jù)時,傳遞的并不是引用本身,依然是值;只是這個 是內(nèi)存地址罷了。

因為把相同的內(nèi)存地址傳過去了,所以對數(shù)據(jù)的操作依然會影響到外部。

所以同理,類似于這樣的代碼也會影響到外部原始數(shù)據(jù):

@Test
 public void testList(){
 List<Integer> list = new ArrayList<>();
 list.add(1);
 addList(list);
 System.out.println(list);
 }

 private void addList(List<Integer> list) {
 list.add(2);
 }

 [1, 2]

那如果是這樣的代碼:

@Test
 public void test02(){
 Car car1 = new Car("benz");
 modifyCar(car1);
 System.out.println(String.format("最終結(jié)果 main car1==%s", car1));
 }

 private void modifyCar(Car car2) {
 System.out.println(String.format("修改之前 car2==%s", car2));
 car2 = new Car("bmw");
 System.out.println(String.format("修改之后 car2==%s", car2));
 }

假設(shè) Java 是引用傳遞那最終的結(jié)果應(yīng)該是打印 bmw 才對。

修改之前 car2==Car{name='benz'}
修改之后 car2==Car{name='bmw'}
最終結(jié)果 main car1==Car{name='benz'}

從結(jié)果又能佐證這里依然是值傳遞。

如果是引用傳遞,原本的 0x1102 應(yīng)該是被直接替換為新創(chuàng)建的 0x1103 才對;而實際情況如上圖所示,car2 直接重新引用了一個對象,兩個對象之間互不干擾。

Go

相對于 Java 來說 Go 的用法又有所不同,不過我們也可以先得出結(jié)論:

Go語言的參數(shù)也是值傳遞。

Go 語言中數(shù)據(jù)類型主要有以下兩種:

值類型與引用類型;

值類型

先以值類型舉例:

func main() {
	a :=10
	modifyValue(a)
	fmt.Printf("最終 a=%v", a)
}

func modifyValue(a int) {
	a = 20
}
輸出:最終 a=10

函數(shù)調(diào)用過程與之前的 Java 類似,本質(zhì)上傳遞到函數(shù)中的值也是 a 的拷貝,所以對其的修改不會影響到原始數(shù)據(jù)。

當(dāng)我們把代碼稍加修改:

func main() {
	a :=10
	fmt.Printf("傳遞之前a的內(nèi)存地址%p \n", &a)
	modifyValue(&a)
	fmt.Printf("最終 a=%v", a)
}
	
func modifyValue(a *int) {
	fmt.Printf("傳遞之后a的內(nèi)存地址%p \n", &a)
	*a = 20
}

傳遞之前a的內(nèi)存地址0xc0000b4040 
傳遞之后a的內(nèi)存地址0xc0000ae020
最終 a=20

從結(jié)果來看最終 a 的值是被方法修改了,這點便是 GoJava 很大的不同點:

Go 中存在著指針的概念,我們可以將變量通過指針的方式傳遞到不同的方法中,在方法里便可通過這個指針訪問甚至修改原始數(shù)據(jù)。

那這么一看不就是引用傳遞嘛?

其實不然,我們仔細(xì)看看剛才的輸出會發(fā)現(xiàn)參數(shù)傳遞前后的內(nèi)存地址并不相同。

傳遞之前a的內(nèi)存地址0xc0000b4040 
傳遞之后a的內(nèi)存地址0xc0000ae020

這也恰好論證了值傳遞,因為這里實際傳遞的是指針的拷貝。

也就是說 modifyValue 方法中的參數(shù)與入?yún)⒌?code>&a都是同一塊內(nèi)存的指針,但指針本身也是需要內(nèi)存來存放的,所以在方法調(diào)用過程中新建了一個指針 a ,從而導(dǎo)致他們的內(nèi)存地址不同。

雖然內(nèi)存地址不同,但指向的數(shù)據(jù)都是同一塊,所以方法內(nèi)修改后原始數(shù)據(jù)也受到了影響。

引用類型

對于 map slice channel 這類引用類型又略有不同:

func main() {
	var personList = []string{"張三","李四"}
	modifySlice(personList)
	fmt.Printf("slice=%v \n", personList)
}
func modifySlice(personList []string) {
	personList[1] = "王五"
}

slice=[張三 王五]

最終我們會發(fā)現(xiàn)原始數(shù)據(jù)也被修改了,但我們并沒有傳遞指針;同樣的特性也適用于 map 。

但其實我們查看 slice 的源碼會發(fā)現(xiàn)存放數(shù)據(jù)的 array 就是指針類型:

type slice struct {
	array unsafe.Pointer
	len int
	cap int
}

所以我們可以直接對數(shù)據(jù)進(jìn)行修改,相當(dāng)于間接的帶了指針。

使用建議

那我們在什么時候使用指針呢?有以下幾點建議:

如果參數(shù)是基本的值類型,比如 int,float 建議直接傳值。如果需要修改基本的值類型,那只能是指針;但考慮到代碼可讀性還是建議將修改后的值返回用于重新賦值。數(shù)據(jù)量較大時建議使用指針,減少不必要的值拷貝。(具體多大可以自行判斷)

Python

Python 中變量是否可變是影響參數(shù)傳遞的重要因素:

如上圖所示,bool int float 這些不可變類型在參數(shù)傳遞過程中是不能修改原始數(shù)據(jù)的。

if __name__ == '__main__':
		x = 1
 modify(x)
 print('最終 x={}'.format(x))	

def modify(val):
 val = 2

最終 x=1

原理與 Java Go中類似,是基于值傳遞的,這里就不再復(fù)述。

這里重點看看可變數(shù)據(jù)類型在參數(shù)傳遞中的過程:

if __name__ == '__main__':
		x = [1]
 modify(x)
 print('最終 x={}'.format(x))	

def modify(val):
 val.append(2)

最終 x=[1, 2]

最終數(shù)據(jù)受到了影響,那么就表明這是引用傳遞嘛?再看個例子試試:

if __name__ == '__main__':
		x = [1]
 modify(x)
 print('最終 x={}'.format(x))	

def modify(val):
 val = [1, 2, 3]

最終 x=[1]

顯而易見這并不是引用傳遞,如果是引用傳遞最終 x 應(yīng)當(dāng)?shù)扔?[1, 2 ,3]

從結(jié)果來看這個傳遞過程非常類似 Go 中的指針傳遞,val 拿到的也是 x 這個參數(shù)內(nèi)存地址的拷貝;他們都指向了同一塊內(nèi)存地址。

所以對這塊數(shù)據(jù)的修改本質(zhì)上改的是同一份數(shù)據(jù),但一旦重新賦值就會創(chuàng)建一塊新的內(nèi)存從而不會影響到原始數(shù)據(jù)。

Java 中的上圖類似。

所以總結(jié)下:

  • 對于不可變數(shù)據(jù):在參數(shù)傳遞時傳遞的是值,對參數(shù)的修改不會影響到原有數(shù)據(jù)。
  • 對于可變數(shù)據(jù):傳遞的是內(nèi)存地址的拷貝,對參數(shù)的操作會影響到原始數(shù)據(jù)。

這么說來這三種都是值傳遞了,那有沒有引用傳遞的語言呢?

當(dāng)然,C++是支持引用傳遞的:

#include <iostream>
using namespace std;
 
class Box
{
 public:
 double len;
};

void modify(Box& b);
 
int main ()
{
	Box b1;
	b1.len=100;
	cout << "調(diào)用前,b1 的值:" << b1.len << endl;
	modify(b1);
	cout << "調(diào)用后,b1 的值:" << b1.len << endl;
	return 0;
}
 
void modify(Box& b)
{
	b.len=10.0;
	Box b2;
	b2.len = 999;
	b = b2;
 
	return;
}

調(diào)用前,b1 的值:100
調(diào)用后,b1 的值:999

可以看到把新對象 b2 賦值給入?yún)?b 后是會影響到原有數(shù)據(jù)的。

總結(jié)

其實這幾種語言看下來會發(fā)現(xiàn)他們中也有許多相似之處,所以通常我們在掌握一門語言后也能快速學(xué)習(xí)其他語言。

但往往是這些基礎(chǔ)中的基礎(chǔ)最讓人忽略,希望大家在日常編碼時能夠考慮到這些基礎(chǔ)知識多想想一定會寫出更漂亮的代碼(bug)。

到此這篇關(guān)于多種語言參數(shù)傳遞原理解析(java、go、python、c++)的文章就介紹到這了,更多相關(guān)java參數(shù)傳遞原理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Python操作ES的方式及與Mysql數(shù)據(jù)同步過程示例

    Python操作ES的方式及與Mysql數(shù)據(jù)同步過程示例

    這篇文章主要為大家介紹了?Python操作Elasticsearch的兩種方式及與Mysql數(shù)據(jù)同步過程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-04-04
  • MobaXterm快速入門、高級使用技巧圖文詳解

    MobaXterm快速入門、高級使用技巧圖文詳解

    MobaXterm 提供豐富的自定義選項,以滿足個人偏好和需求,您可以自定義外觀、鍵盤快捷鍵、字體、顏色方案等,這篇文章主要介紹了MobaXterm快速入門、高級使用技巧,需要的朋友可以參考下
    2023-06-06
  • windows開發(fā)記事本程序紀(jì)實(二)邏輯篇1

    windows開發(fā)記事本程序紀(jì)實(二)邏輯篇1

    從本節(jié)開始介紹windows開發(fā)實現(xiàn)記事本程序的邏輯實現(xiàn)部分。本節(jié)的主要內(nèi)容有以下3點:1. 主窗口定義2. RichEdit控件的選用及初始化3. 整個程序ICON的選擇
    2014-08-08
  • BurpSuite超詳細(xì)安裝和基礎(chǔ)使用教程(已破解)

    BurpSuite超詳細(xì)安裝和基礎(chǔ)使用教程(已破解)

    Burp?Suite?是用于攻擊web?應(yīng)用程序的集成平臺包含了許多Burp工具,它主要用來做安全性滲透測試,可以實現(xiàn)攔截請求、Burp?Spider爬蟲、漏洞掃描(付費)等類似Fiddler和Postman但比其更強(qiáng)大的功能,今天給大家介紹下BurpSuite安裝破解使用教程,感興趣的朋友一起看看吧
    2022-10-10
  • 淺析mmdetection在windows10系統(tǒng)環(huán)境中搭建過程

    淺析mmdetection在windows10系統(tǒng)環(huán)境中搭建過程

    這篇文章主要介紹了mmdetection在windows10系統(tǒng)環(huán)境中搭建過程,本文圖文并茂通過實例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-01-01
  • RPC、RMI、SOAP的區(qū)別詳解

    RPC、RMI、SOAP的區(qū)別詳解

    這篇文章主要介紹了RPC、RMI、SOAP的區(qū)別詳解,本文還同時講解了RPC、SOAP、WSDL的關(guān)系,需要的朋友可以參考下
    2015-07-07
  • Fiddler4的安裝與使用詳解

    Fiddler4的安裝與使用詳解

    這篇文章主要介紹了Fiddler4的安裝與使用詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • DeepSeek本地部署+可視化WebUI的實現(xiàn)(圖文教程)

    DeepSeek本地部署+可視化WebUI的實現(xiàn)(圖文教程)

    本文主要介紹了在本地部署DeepSeek并使用可視化WebUI進(jìn)行AI輔助,文中通過圖文介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-02-02
  • 詳解MD5算法的原理以及C#和JS的實現(xiàn)

    詳解MD5算法的原理以及C#和JS的實現(xiàn)

    MD5?是哈希算法(散列算法)的一種應(yīng)用。這篇文章主要和大家介紹一下MD5算法的原理以及C#和JS的實現(xiàn),文中的示例代碼講解詳細(xì),需要的可以參考一下
    2023-03-03
  • 鴻蒙開發(fā)搭建flutter適配的開發(fā)環(huán)境

    鴻蒙開發(fā)搭建flutter適配的開發(fā)環(huán)境

    文章詳細(xì)介紹了在Windows系統(tǒng)上如何創(chuàng)建和運行鴻蒙Flutter項目,包括使用flutter?doctor檢測環(huán)境、創(chuàng)建項目、編譯HAP包以及在真機(jī)上運行項目,打包鴻蒙Flutter應(yīng)用的測試包和正式包的方法,并介紹了常見問題的解決方法
    2024-12-12

最新評論