本教程使用的開發環境是一款在線編譯器——ChainIDE,具體的使用方法在之前的文章當中已經有講解過,有需要的同學可以自行查看。
序言
以太坊的EVM就像四十年前的計算機一樣,由於儲存和計算的代價受制於全網的帶寬,需要對每次數據拷貝非常謹慎,我們對超過256位的變量(數組、結構體)儲存往往需要指定它的儲存位置。
同時由於它是一種靜態語言,每一個變量的類型都必須定義清楚,包括他的儲存位置,不同儲存類型的變量,賦值的方式也是不一樣的。
數組與結構體
數組。簡單理解來說可以是由多個相同的值類型構成的序列,序列的長度由用戶設定,或者可以設定爲可變長度。
如果數組的儲存地址爲storage的話,那麼值類型可以爲任何類型,如果儲存地址爲memory,則類型受到ABI類型的限制,不能爲address payable、contract、enum和struct。
我們通過一些例程來簡單瞭解一下數組,包括如何建立數組與遍歷數組。
pragma solidity >=0.4.22 <0.6.0;
contract Array{
//設定固定長度uint數組並進行初始化
uint[5] a = [1,2,3,5,5]; //設定一個長度爲5的uint數組,名字叫a
function get_a_value() view public returns(uint)
{
return a[1]; //返回值爲2
}
bytes9 b = 0x6c690338656363468e; //定義一個長度爲9字節的值
byte[] b1; //定義一個值類型爲byte的變長數組
//向變長數組內輸出
function set_b1_array() public returns(byte)
{
for (uint i=0;i<=b.length;i++) //b.length指的是b數組的長度值
{
b1.push(b[i]); //向數組b1內添加值
}
return b[1]; //返回值爲69
}
}
數組的定義方式就如上面例程所示,一開始設定值類型,然後在後面加入一個方括號,方括號內是數組的長度,如果要定義變長數組則方括號內爲空,接着加入一個空格,最後是數組的名字。
uint[5] a;
數組類型有個可以調用的變量叫length,這個變量代表了數組的長度,可以通過這個變量來對數組進行遍歷和清空。
變長數組可以調用push函數在數組的尾部添加值,對於在全局設定的狀態變量來說,這種值的修改會使用到很多gas,使用時需慎重。
結構體。結構體就好像一個袋子裏有很多的貨物一樣,一個結構體內可以包含很多變量,每個變量都是屬於這個結構體的一部分。
結構體的keyword是struct,具體如何定義和調用可以通過下面的例程進行學習:
//將結構體設置爲狀態變量
uint eth_score = 100;
Program eth=Program //創建一個結構體 名字爲eth
(0x797206393eB6582ac86883fA623CB5A05021191D,
eth_score,
100,
false); //在定義結構體時必須將初始化做好,對於賦值爲常量的值時,對應的儲存位置爲storage
function set_eth_score(uint set_score) public {
eth.score = set_score;
}
function get_eth_score() public view returns(uint)
{
return eth.score;
}
//將結構體設置爲局部變量
function get_program_param() public pure returns (uint)
{
Program memory chainide=Program
({contract_address:0x797206393eB6582ac86883fA623CB5A05021191D,
member_num: 10,
score:99,
capitalize:true}); //在定義結構體時必須將初始化做好,對於賦值爲常量的值時,對應的儲存位置爲memory
return(chainide.score);
}
這個例程分別對將結構體設置爲狀態變量(storage),以及設置爲局部變量(memory)進行了分別舉例。
Solidity的語言規範是要在定義結構體時對這個結構體進行初始化,定義初始化有兩種方式,一種是根據之前定義的結構體順序進行變量的初始化定義,第二種是通過{key:word}的方式進行每個元素的初始化。
tips:通過eth.score的方式可以調用結構體內變量的值,同時也可以根據之前定義的值類型在returns當中進行設置,但是要注意的是在現在的solidity當中自定義的結構體類型是不可以作爲函數的返回值。
Storage和memory
在Solidity當中有兩個關鍵字memory(臨時儲存的變量),storage(永久儲存的變量),這兩個關鍵字在合約內有一些約定俗成的定義,我們可以先通過一個例程來簡單瞭解一下。
pragma solidity ^0.5.6;
contract Person {
int public age1; //狀態變量 儲存在storage當中
string public name1;
function Person1(int age,string memory name) public{
age1 = age; //局部變量 儲存在memory當中
name1 = name;
}
function Person2 (string memory name3) pure public returns(string memory){ //局部變量 儲存在memory當中
string memory name2 = name3;
return (name2); //局部變量 儲存在memory當中
}
}
}
通過在編寫程序時,在變量類型和名字的中間加入memory和storage關鍵字,來對變量的存儲位置進行定義。有些定義是默認的,比如在function作用域以外的變量基本上都是通過storage進行存儲的,而函數的輸入參數和輸出參數基本上是memory。
如果函數當中對儲存位置爲storage的變量進行改變,就會需要用到gasfee,因爲storage的數值是存儲在區塊鏈上的,因此在寫程序時需要儘量減少對儲存類型爲storage的變量的賦值。
如果對儲存位置爲memory的變量進行賦值和調用,則是不需要用到gas的,如果函數內都是這種操作,則可以在函數的()後加上pure定義符,表示這個調用這個函數不需要用到gas。
有些更加深入的探索,比如這兩個不同儲存位置的變量之間的相互賦值,會如何影響對方的值,以及這種storage的指針是怎麼運行的,在參考資料當中有涉及,有興趣的朋友可以自己點擊查詢。
由於在不同版本的Solidity當中,對於變量的定義和使用的規則是不一樣的,因此建議大家在編寫程序的時候使用統一的編譯器版本。
今天的教程到這裏就結束了,希望大家有所收穫。