對(duì)引用的再次辨析
????對(duì)solidity中的引用問(wèn)題總是想再說(shuō)說(shuō),再說(shuō)說(shuō)。前天直播講了講,感覺(jué)還不錯(cuò),算是說(shuō)明白了,在寫幾句話,總結(jié)的更精煉一些。
????兩個(gè)算法
????對(duì)于引用類型變量的賦值操作:
????x = a
????其實(shí)有兩件事情要做:
????1. 確定它的賦值語(yǔ)義:是引用拷貝還是值拷貝
????2. 如果是值拷貝,檢查它是否允許
? ? 這兩個(gè)算法上次的文章講過(guò)了。第一個(gè)算法供我們判斷賦值操作干了什么事情,第二個(gè)算法讓我們理解這個(gè)賦值操作沒(méi)通過(guò)編譯檢查的原因。
????對(duì)這兩個(gè)算法我們今天再做一個(gè)辨析。
????關(guān)于判定算法
????solidity的引用類型變量賦值被兩個(gè)因素影響:
????1. 其成員變量相比一般面向?qū)ο笳Z(yǔ)言的對(duì)象的成員變量的特殊之處在于,java對(duì)象的成員變量在內(nèi)存中,而合約的成員變量在持久化存儲(chǔ)中,這使得合約成員變量的這個(gè)“引用”的含義發(fā)生了變化:它永遠(yuǎn)引用到固定的數(shù)據(jù)塊。它不可能再去指向別的數(shù)據(jù)塊。既然如此,當(dāng)x是成員變量,只能是值拷貝操作。
????2. 變量的location對(duì)賦值操作的影響是:它將其他語(yǔ)言中統(tǒng)一的數(shù)據(jù)存在空間分割成三個(gè)子空間(calldata memory storage),當(dāng)賦值操作穿過(guò)子空間的邊界,就必須是賦值操作了。具體的說(shuō),一種location的變量只能在一個(gè)子空間內(nèi)切換指向不同數(shù)據(jù)塊,但不能跑出這個(gè)子空間,跨子空間的賦值,也就是x和a的location不同時(shí),必須、只能解釋為賦值,這正是solidity編譯器和evm運(yùn)行時(shí)做的事情。
????這是判定算法的完整解釋。
????關(guān)于檢查算法
????檢查算法是在判定算法完成,輸出了結(jié)果之后執(zhí)行的。如果判定為引用拷貝,那就不必檢查了,因?yàn)闄z查的兩件事情:calldata數(shù)據(jù)塊只讀和mapping不能遍歷從而無(wú)法實(shí)現(xiàn)數(shù)據(jù)拷貝,在引用拷貝中都不會(huì)發(fā)生,所以沒(méi)有檢查的必要。
? ? 判定算法輸出值拷貝時(shí),關(guān)于calldata的檢查是,x不能是calldata的。這不言而喻,因?yàn)槟銦o(wú)法向message數(shù)據(jù)體的calldata數(shù)據(jù)域中拷東西,正如你不能修改msg.sender或msg.value。但值拷貝右側(cè)的a是calldata沒(méi)有問(wèn)題。
????判定算法輸出值拷貝時(shí),關(guān)于mapping的檢查更嚴(yán)格:x和a的類型必須與mapping無(wú)關(guān),即它本身不是mapping也不能嵌套擁有mapping的成分。mapping不能拷貝的原因不再贅述。