bytes32 中的字節與 bytes 結構中的字節之間的區別
可以在文件中閱讀:
A
bytes
類似於byte[]
,但它被緊緊地封裝在呼叫數據和記憶體中。這到底是什麼意思?例如,根據此連結,我們為什麼要使用以下方法將
bytes
對象轉換為bytes32
:function bytesToBytes32(bytes b, uint offset) private pure returns (bytes32) { bytes32 out; for (uint i = 0; i < 32; i++) { out |= bytes32(b[offset + i] & 0xFF) >> (i * 8); } return out; }
並且不使用:
function bytesToBytes32(bytes b, uint offset) private pure returns (bytes32) { bytes32 out; for (uint i = 0; i < 32; i++) { out[i] = b[offset + i]; } return out; }
此外:
- 為什麼不能
bytes32
單獨設置結構的元素,但可以使用bytes
?- 如果我想通過將字節(字面意思是字節,而不是類型
bytes
)視為 來對它們進行一些計算,uint8
那麼它是否可以通過簡單地轉換為兩種結構來b[i]
工作uint8(b[i])
?例如,為了uint256
從這些類型中獲取一個,可以使用以下程式碼:uint res; for (uint i = 0; i < 32; i++) { res += uint8(b[i]) * (256 ** i); }
工作,是
b
一個對bytes
像還是bytes32
一個?最後,是uint res = uint(b[i]);
和uint res = uint(uint8(b[i]));
兩個等價的程式碼嗎?
EVM 的基本工作單元,通常稱為一個字,是 32 字節。這意味著對於 EVM,無論我們要表達 1 還是 2^150,都需要 32 個字節或 256 位來完成。
EVM 中的所有內容都需要消耗氣體,包括記憶體,因此單詞越少越好。
一個字節類似於字節
$$ $$,但它被緊緊地包裝在 calldata 和 memory 這到底是什麼意思?
在這裡,緊密打包意味著不是每個項目使用一個單詞(32 字節),而是編譯器將“打包”多個項目以共享一個單詞。
讓我們看一個例子。假設我們要將數字 12345 表示為字節,這給出
$$ 48, 57 $$大端(48 * 16^2 + 57 = 12345)。如果我們不打包字節,這會給我們: $$ 48, 0,…31 times, 57, 0,…31 times $$ 現在,如果我們打包字節,我們可以將其表示為: $$ 48, 57, 0,…30 times $$ 如您所見,使用打包表示減少了我們需要使用的單詞數量。字節尤其如此,因為我們在沒有打包的情況下每個字浪費了 31 個字節。
為什麼不能單獨設置 bytes32 結構的元素,但可以使用字節?
我認為這更像是 Solidity 開發人員的設計選擇,而不是其他任何東西。我不完全確定它背後的基本原理,但我可以想像這
bytes32
主要是為了保存短字元串並使它們不可變允許一些優化,否則這些優化可能會很乏味。考慮到這一點,使用的程式碼片段
out[i] = b[offset + i]
理論上可以工作,但根本不是有效的 Solidity 程式碼。第一個片段有效,但我認為有點尷尬。b
$$ offset + i $$是一個字節,所以用它來屏蔽它
0xff
是一個空操作。該行可以簡單地寫成out |= bytes32(b[offset + i]) >> (i * 8);
我很確定這在語義上是等效的,但如果我遺漏了什麼,請糾正我。
請注意,此轉換與是否打包的字節並不真正相關。
如果我想通過將它們視為 uint8 來對字節(字面意思是字節,而不是類型字節)進行一些計算,那麼它是否可以通過簡單地轉換 b 來適用於這兩種結構?
$$ i $$到 uint8(b$$ i $$)
是的,uint8 和一個字節都代表 8 位數據並且具有相同的範圍,所以這應該沒有任何問題。
最後,是 uint res = uint(b
$$ i $$); 和 uint res = uint(uint8(b$$ i $$)); 兩個等效程式碼?
理論上兩者應該是等價的,但 Solidity 編譯器,至少在目前版本中,不允許將字節
uint
直接轉換為 a,因此需要顯式轉換為uint8
。