Solidity數據位置
- 所有復雜的數據類型,即數組、結構和映射類型,都會有一個額外屬性“數據位置”,用來指定數據的存儲位置,即數據是存儲在memory還是存儲在storage里面
- 根據上下文環境,IDE會自動指定數據的默認存儲位置,但是也可以通過在類型名字之后添加關鍵字stirage或者memory進行修改
- 函數參數(包括返回的參數)的數據位置默認是memory,局部變量的數據存儲位置默認是storage,狀態變量的數據位置強制是storage
- 另外還有第三種數據存儲位置,calldata,這個是一塊只讀的,不會永久存儲的位置,用來存儲函數的參數。外部函數的參數(非返回函數)的數據位置會被強制指定為calldata,效果和memory差不多。
數據位置總結
強制
- 外部函數的參數(不包括返回的參數):calldata
- 狀態變量:storage默認存儲位置
可變
- 函數參數(包括返回參數):memory
- 引用類型的局部變量:storage,例,動態數組使用哈希表,要求很大的存儲空間,遍歷key和value是可能的,防止哈希碰撞。
- 值類型的局部變量:棧(stack)
特別要求
- 公開可見(publicly visible)的函數參數一定是memory類型,如果要求是storage類型,則必須是private或者internal函數。這個的目的是為了防止公開調用占用資源。
- memory和storage只要數據類型一致,就可以互相傳數值。如果是memory傳給storage,是對于數據的完整拷貝傳過去,不是簡簡單單的引用。同樣是storage,如果是狀態變量就會改寫原先內容,相當于拷貝,存儲到永久性區間里面。如果是局部變量,不管對方是狀態變量還是局部變量都是引用?
例子
- 講解
- 類型之間都是storage,那么就是引用,如果類型不同,就是復制然后操作。uint[] storage d是一個引用,相當于C語言中的指針,指向data1和data2
- 函數參數默認是memory,需要將其改成stroage類型
- 將public改成internal類型,防止公開調用,占用大量的資源
- 代碼
pragma solidity ^0.4.0;
contract C{uint[] public data1;uint[] public data2;function append1() public {append(data1);}function append2() public {append(data2);}function append(uint[] storage d)internal{d.push(23);}
}
- 界面
糾錯(1)
代碼
pragma solidity ^0.4.0;
contract C{uint public a;uint[] public data;function f() public {uint[] storage x;x.push(2);data=x;}
}
界面
問題
-
a變成了一個計數器,這個是Solidity的缺陷,原因在于uint[] storage x;它是一個指針,如果沒有賦值,默認指定合約地址的整個存儲空間的0位置,也就是uint public a 的位置。
-
uint[] storage x,指向變量a。每次調用f函數,x 的長度就會增加,并且將存儲的長度存儲在變量a上,因此每次a的數值每次增加1.
-
如果修改代碼如下
pragma solidity ^0.4.0;
contract C{uint public a=23;uint[] public data;function g(uint input)public{a = input;}function f() public {uint[] storage x;x.push(2);data=x;}
}
- 首先取a的值,為23,點擊f函數,再點擊a,得到a變成24,24之前的數據不可以訪問,但是24是存儲的2,由代碼x.push(2)來完成。g函數也是一樣的效果。
修正
- 給uint[] storage x;初始化指定默認位置,比如改成uint[]?x = data;x.push(2);刪除data = x;這一句