Bytes

彙編操作碼

  • September 15, 2018

我需要一些幫助來理解彙編函式

pragma solidity ^0.4.23;
contract Test {
   function addTest() public pure returns (uint c) {
   bytes memory b = new bytes(1024);
   assembly { c:=add(b, 0x20) }
}
}

在這個片段中,添加一個數字和一個字節數組是什麼意思?!

此外,無論我採用什麼字節變數的大小,函式的結果始終為 160。這意味著 b 以某種方式被評估為 128?為什麼 b 在 add 函式參數中被視為 128 任何幫助表示讚賞。謝謝!

函式 addTest() 中的這個智能合約的作用是:

  1. 它在記憶體中初始化一個字節數組(易失性),大小為 1024 字節
  2. 彙編:它獲取字節數組的地址,而不是內容,而是您可以在記憶體中找到其內容的地址,並將 0x20 添加到它
  3. (memory location of) b + 0x20 的結果儲存在c

為什麼 b 被評估為 128?如您所知,智能合約已將 b 的地址用於計算。地址 0x80 (128) 有一個特殊用途。首先,您必須知道 EVM 中的記憶體分配是通過讀取地址 0x40(“空閒記憶體指針”)中的值,添加所需的字節數並將其再次儲存在 0x40 中來進行的。所以 0x40 包含一個地址,它告訴我們空閒記憶體從哪裡開始(有關更多詳細資訊,請查看乙太坊操作碼:前幾條指令的含義?)。在您的智能合約被編譯器額外插入的程式碼初始化後,位於 0x40 的空閒記憶體指針設置為 0x80。換句話說,當你的智能合約開始工作時,它可以使用的第一個記憶體位置是 0x80 (https://solidity.readthedocs.io/en/v0.4.25/miscellaneous.html#layout-in-memory)。這就是為什麼你總是得到結果 160, 0xa0 = 0x80 + 0x20

我為您提供了一個範例,它初始化了 bytearray b的三個字節並將它們以彙編的形式讀取到返回值中:

pragma solidity ^0.4.25;

contract Test {
   function addTest() public pure returns (byte c1, byte c2, byte c3) {
       // create bytes array and init first byte
       bytes memory b = new bytes(169);
       b[0] = byte(59);
       b[1] = byte(42);
       b[2] = byte(99);

       assembly {
           // load byte 0-31 to read b[0]
           // note: first 32 bytes contain the array length
           c1 := mload(add(b, 0x20))
           // load byte 1-32 to read b[1]
           c2 := mload(add(b, 0x21))
           // load byte 2-33 to read b[2]
           c3 := mload(add(b, 0x22))
       }
   }
}

在前面的範例中,您可以看到我開始從地址 b + 0x20 處讀取數據*。這是因為 bytearray 的長度儲存在地址b*的前 32 個字節。

如您所見,每次我想讀取一個字節時,我都會載入 32 個字節。由於 mload(…) 指令相對於 gas 成本來說很便宜,所以這樣做並不重要。然而,就執行時而言,它有點昂貴。您可以通過屏蔽前 32 個字節來優化程式碼,以提取其中的每個所需字節:

pragma solidity ^0.4.25;

contract Test {
   function addTest() public pure returns (byte c1, byte c2, byte c3) {
       // create bytes array and init first byte
       bytes memory b = new bytes(169);
       b[0] = byte(59);
       b[1] = byte(42);
       b[2] = byte(99);

       assembly {
           // load byte 0-31 to read b[0]
           // note: first 32 bytes contain the array length
           let data32b := mload(add(b, 0x20))
           c1 := data32b
           // read b[1] from byte 0-31
           c2 := mul(data32b, 256)
           // read b[2] from byte 0-31
           c3 := mul(data32b, exp(256, 2))
       }
   }
}

當您使用 sload(…) 命令從儲存(持久)載入數據時,請記住使用第二個變體,不要在這種情況下使用第一個變體。這是因為 sload(…) 在 gas 和執行時間方面非常昂貴。

最後一點:當您的 EVM 與 constantinople 版本兼容時,您可以將 exp(…) 替換為 shl(…)(左移),這也可以節省一些氣體:

pragma solidity ^0.4.25;

contract Test {
   function addTest() public pure returns (byte c1, byte c2, byte c3) {
       // create bytes array and init first byte
       bytes memory b = new bytes(169);
       b[0] = byte(59);
       b[1] = byte(42);
       b[2] = byte(99);

       assembly {
           // load byte 0-31 to read b[0]
           // note: first 32 bytes contain the array length
           let data32b := mload(add(b, 0x20))
           c1 := data32b
           // note: In constantinople you can use the following instead:
           // read b[1] from byte 0-31
           c2 := shl(data32b, 8)
           // read b[2] from byte 0-31
           c3 := shl(data32b, mul(8, 2))
       }
   }
}

引用自:https://ethereum.stackexchange.com/questions/58794