Solidity
將結構推送到包含數組本身的數組
我正在閱讀這篇文章,其中給出了一個不起作用的契約範例。雖然他們已經在文章中修復了它,但我想知道為什麼它不起作用。我在下面複製了一個簡化版本:
pragma solidity ^0.4.18; contract StructArrayInitWrong { struct Room { address[] players; } Room[] rooms; function createRoom() public { address[] adr; adr.push(msg.sender); Room memory room = Room(adr); rooms.push(room); } function getRoomsLength() view returns (uint) { return rooms.length; } }
奇怪的是,在這個合約中
rooms
每次createRoom
呼叫數組長度都會增加兩倍。你能解釋一下這種行為嗎?謝謝。
問題在於這條線
address[] adr;
有一個關於未初始化儲存的“警告”。這裡發生的事情比溫和的警告可能暗示的要多。我真的不明白為什麼這不是一個硬錯誤,所以開發人員不接受它。
由於儲存未初始化,編譯器不知道將動態數組放在哪裡,
adr[]
因此將其放在第一個 slot中。你沒看錯。它在跺腳rooms[]
。哎喲!由於該槽的佔用者也恰好是一個動態數組,因此它也使用第一個詞來描述數組長度。因此,我們得到了觀察到的奇怪行為……推到一個數組,然後推到另一個,然後噗!- 兩個數組的長度都是 2。
這不是在函式內部而不是在通常的位置(外部)聲明的儲存導致潛在的災難性數據覆蓋的唯一情況。另一個例子見這裡:Solc 編譯器監督?不適當的映射聲明會覆蓋儲存
考慮到智能合約應該是清晰且沒有缺陷的,我不喜歡這種意想不到的結果。這和參考變數加起來對我來說有點太多巫術了。
也許其他人會加入更好的啟發式方法。我可能建議養成兩個習慣
- 始終將狀態變數佈局在相同的位置,靠近頂部,外部函式,以避免這種意外覆蓋。
- 永遠不要調整/設置以前從索引儲存中讀取的變數,因為它可能會意外覆蓋儲存。見這裡:http: //vessenes.com/solidity-frustrations-references-and-mapping/
這是一個更改並按預期工作的程式碼:
pragma solidity ^0.4.18; contract StructArrayInitWrong { struct Room { address[] players; } Room[] rooms; address[] adr; // <=== if we're going to store this, then let's store this. function createRoom() public { // <=== note gaping hole adr.push(msg.sender); Room memory room = Room(adr); rooms.push(room); } function getRoomsLength() public view returns (uint) { return rooms.length; } }
希望能幫助到你。