本教程使用的開發(fā)環(huán)境是一款在線編譯器——ChainIDE,具體的使用方法在之前的文章當中已經(jīng)有講解過,有需要的同學可以自行查看。
網(wǎng)址:https://eth.chainide.com/
序言
以太坊的EVM就像四十年前的計算機一樣,由于儲存和計算的代價受制于全網(wǎng)的帶寬,需要對每次數(shù)據(jù)拷貝非常謹慎,我們對超過256位的變量(數(shù)組、結(jié)構(gòu)體)儲存往往需要指定它的儲存位置。
同時由于它是一種靜態(tài)語言,每一個變量的類型都必須定義清楚,包括他的儲存位置,不同儲存類型的變量,賦值的方式也是不一樣的。
數(shù)組與結(jié)構(gòu)體
數(shù)組。簡單理解來說可以是由多個相同的值類型構(gòu)成的序列,序列的長度由用戶設(shè)定,或者可以設(shè)定為可變長度。
如果數(shù)組的儲存地址為storage的話,那么值類型可以為任何類型,如果儲存地址為memory,則類型受到ABI類型的限制,不能為address payable、contract、enum和struct。
我們通過一些例程來簡單了解一下數(shù)組,包括如何建立數(shù)組與遍歷數(shù)組。
pragma solidity >=0.4.22 <0.6.0;
contract Array{
//設(shè)定固定長度uint數(shù)組并進行初始化
uint[5] a = [1,2,3,5,5]; //設(shè)定一個長度為5的uint數(shù)組,名字叫a
function get_a_value() view public returns(uint)
{
return a[1]; //返回值為2
}
bytes9 b = 0x6c690338656363468e; //定義一個長度為9字節(jié)的值
byte[] b1; //定義一個值類型為byte的變長數(shù)組
//向變長數(shù)組內(nèi)輸出
function set_b1_array() public returns(byte)
{
for (uint i=0;i<=b.length;i++) //b.length指的是b數(shù)組的長度值
{
b1.push(b[i]); //向數(shù)組b1內(nèi)添加值
}
return b[1]; //返回值為69
}
}
數(shù)組的定義方式就如上面例程所示,一開始設(shè)定值類型,然后在后面加入一個方括號,方括號內(nèi)是數(shù)組的長度,如果要定義變長數(shù)組則方括號內(nèi)為空,接著加入一個空格,最后是數(shù)組的名字。
uint[5] a;
數(shù)組類型有個可以調(diào)用的變量叫length,這個變量代表了數(shù)組的長度,可以通過這個變量來對數(shù)組進行遍歷和清空。
變長數(shù)組可以調(diào)用push函數(shù)在數(shù)組的尾部添加值,對于在全局設(shè)定的狀態(tài)變量來說,這種值的修改會使用到很多gas,使用時需慎重。
結(jié)構(gòu)體。結(jié)構(gòu)體就好像一個袋子里有很多的貨物一樣,一個結(jié)構(gòu)體內(nèi)可以包含很多變量,每個變量都是屬于這個結(jié)構(gòu)體的一部分。
結(jié)構(gòu)體的keyword是struct,具體如何定義和調(diào)用可以通過下面的例程進行學習:
//將結(jié)構(gòu)體設(shè)置為狀態(tài)變量
uint eth_score = 100;
Program eth=Program //創(chuàng)建一個結(jié)構(gòu)體 名字為eth
(0x797206393eB6582ac86883fA623CB5A05021191D,
eth_score,
100,
false); //在定義結(jié)構(gòu)體時必須將初始化做好,對于賦值為常量的值時,對應的儲存位置為storage
function set_eth_score(uint set_score) public {
eth.score = set_score;
}
function get_eth_score() public view returns(uint)
{
return eth.score;
}
//將結(jié)構(gòu)體設(shè)置為局部變量
function get_program_param() public pure returns (uint)
{
Program memory chainide=Program
({contract_address:0x797206393eB6582ac86883fA623CB5A05021191D,
member_num: 10,
score:99,
capitalize:true}); //在定義結(jié)構(gòu)體時必須將初始化做好,對于賦值為常量的值時,對應的儲存位置為memory
return(chainide.score);
}
這個例程分別對將結(jié)構(gòu)體設(shè)置為狀態(tài)變量(storage),以及設(shè)置為局部變量(memory)進行了分別舉例。
Solidity的語言規(guī)范是要在定義結(jié)構(gòu)體時對這個結(jié)構(gòu)體進行初始化,定義初始化有兩種方式,一種是根據(jù)之前定義的結(jié)構(gòu)體順序進行變量的初始化定義,第二種是通過{key:word}的方式進行每個元素的初始化。
tips:通過eth.score的方式可以調(diào)用結(jié)構(gòu)體內(nèi)變量的值,同時也可以根據(jù)之前定義的值類型在returns當中進行設(shè)置,但是要注意的是在現(xiàn)在的solidity當中自定義的結(jié)構(gòu)體類型是不可以作為函數(shù)的返回值。
Storage和memory

在Solidity當中有兩個關(guān)鍵字memory(臨時儲存的變量),storage(永久儲存的變量),這兩個關(guān)鍵字在合約內(nèi)有一些約定俗成的定義,我們可以先通過一個例程來簡單了解一下。
pragma solidity ^0.5.6;
contract Person {
int public age1; //狀態(tài)變量 儲存在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關(guān)鍵字,來對變量的存儲位置進行定義。有些定義是默認的,比如在function作用域以外的變量基本上都是通過storage進行存儲的,而函數(shù)的輸入?yún)?shù)和輸出參數(shù)基本上是memory。
如果函數(shù)當中對儲存位置為storage的變量進行改變,就會需要用到gasfee,因為storage的數(shù)值是存儲在區(qū)塊鏈上的,因此在寫程序時需要盡量減少對儲存類型為storage的變量的賦值。
如果對儲存位置為memory的變量進行賦值和調(diào)用,則是不需要用到gas的,如果函數(shù)內(nèi)都是這種操作,則可以在函數(shù)的()后加上pure定義符,表示這個調(diào)用這個函數(shù)不需要用到gas。
有些更加深入的探索,比如這兩個不同儲存位置的變量之間的相互賦值,會如何影響對方的值,以及這種storage的指針是怎么運行的,在參考資料當中有涉及,有興趣的朋友可以自己點擊查詢。
由于在不同版本的Solidity當中,對于變量的定義和使用的規(guī)則是不一樣的,因此建議大家在編寫程序的時候使用統(tǒng)一的編譯器版本。
今天的教程到這里就結(jié)束了,希望大家有所收獲。