2018-10-24 筆記
在Solidity - 引用類型中提到,當(dāng)沒有給局部變量的uint[] storage p賦值的時(shí)候,p會(huì)默認(rèn)指向storage的第一個(gè)slot。
首先,slot的英文翻譯是指容器的插槽、投幣口,暫時(shí)先不解釋這個(gè)東西。先看看storage,官方對(duì)storage的解釋是
Every account has a persistent key-value store mapping 256-bit words to 256-bit words called storage.
中文意思是,storage是一個(gè)持久化的存儲(chǔ)區(qū),它將32字節(jié)的鍵映射到32字節(jié)的鍵。
簡單理解就是,storage是一個(gè)超大的數(shù)組,數(shù)組可以看成儲(chǔ)物柜,slot就是一個(gè)個(gè)的小柜子。

其中每一個(gè)slot的大小是32字節(jié),slot0的地址(即storage的起始地址)是0x00。Solidity對(duì)聲明時(shí)候沒有進(jìn)行初始化的變量,都默認(rèn)值為0。因此p作為一個(gè)指針,其中指針的值就是內(nèi)存的地址(這點(diǎn)與C語言指針一樣),因此它會(huì)指向slot0。至于為什么a的值變了,當(dāng)然就是因?yàn)?code>a存儲(chǔ)在了slot0。
下面整理一下storage內(nèi)存布局的幾條規(guī)則:
- 變量位置按照聲明的順序,從
slot0開始排序。因此a存儲(chǔ)在了slot0。 - 變量占用的內(nèi)存大小與他定義的類型一致,也就是
uint256占用32個(gè)字節(jié),uint8占用1個(gè)字節(jié)。 - 如果一個(gè)變量不能完全存在一個(gè)
slot中,那么他就從下一個(gè)slot開始存儲(chǔ)。
uint8 a; //存儲(chǔ)在slot0
uint256 b; //因?yàn)閟lot0剩余的空間不夠存下一個(gè)uint256,則從slot1開始存儲(chǔ),另外uint 相當(dāng)于 uint256
-
structs和arrays總是在一個(gè)新的slot中開始存儲(chǔ),并且占用整個(gè)slot,但每一個(gè)元素是緊緊相挨的。
pragma solidity ^0.4.0;
contract hello {
struct items {
uint8 a;
uint8 b;
}
items[3] public items_arr;
}
items_arr數(shù)組有三個(gè)元素,他們的內(nèi)存占用情況是,首先struct items的內(nèi)存占用是2字節(jié),然后按照一個(gè)struct占用一個(gè)slot的話,那么items_arr占用了3*32的字節(jié),其中很多都浪費(fèi)了。(這個(gè)其實(shí)我自己也不太肯定,因?yàn)槭褂肦emix調(diào)試的時(shí)候,看不到storage內(nèi)存詳情,也許要通過指令集來判斷,這里暫時(shí)存疑,以后驗(yàn)證再補(bǔ)充。XXX)
Arrays的存儲(chǔ)方式
arrays有兩種類型,靜態(tài)長度uint[3]與動(dòng)態(tài)長度uint[]。靜態(tài)長度的話,在編譯的時(shí)候,就可以確定他的內(nèi)存占用大小,因此可以提前分配。但是動(dòng)態(tài)長度的array,他的元素大小是不確定的,因此需要使用別的方式來存儲(chǔ)動(dòng)態(tài)長度的數(shù)組。舉個(gè)例子:
pragma solidity ^0.4.0;
contract hello {
uint a;
uint[] public b;
uint c;
uint[3] d;
}
其中a的位置是slot0,那么b的位置是slot1,c的位置是slot2,d的位置是slot3-slot5??梢钥吹絼?dòng)長數(shù)組只占用了一個(gè)slot,然后這個(gè)位置用來存放動(dòng)長數(shù)組的長度,即b.lenght的值。動(dòng)態(tài)數(shù)組的元素存儲(chǔ)在其他的位置,這個(gè)位置根據(jù)keccak256()方法計(jì)算出來,比如b[1]的位置就是keccak256(1 . p)。其中p是b所在slot1的起始地址0x32(因?yàn)橐粋€(gè)slot占32個(gè)字節(jié)),另外.代表的是連接符。
Mappings的存儲(chǔ)方式
Mappings也會(huì)占用一個(gè)slot,但是這個(gè)slot是空的,不記錄東西,用來做為尋找元素的基址,尋址方式則跟Arrays的一樣。