類型

翻譯原文
date:20170617

Solidity是靜態(tài)類型語言,這意味著每個變量的類型必須在編譯的時候指定(或者知曉炬称,查看以下類型推斷章節(jié))變量類型汁果。Solidity提供了多種簡單的類型,可以通過簡單類型組合成復雜的類型玲躯。
另外据德,類型可以與包含操作符的表達式互動。瀏覽不同的操作符跷车,參看操作符優(yōu)先級章節(jié)棘利。

值類型

以下的類型通常稱為值類型,因為這些類型的變量通常傳入的是值朽缴。例如在給函數(shù)傳入?yún)?shù)的時候或者在賦值的時候善玫,傳入的是值的拷貝。

布爾類型(Booleans)

bool:可能的值只有true或者false.

操作符:

  • !邏輯非
  • &&邏輯與
  • ||邏輯或
  • ==
  • !=不等

操作符||&&實行的是短路規(guī)則不铆。這意味著:在表達式f(x)||g(y)中蝌焚,如果f(x)的值是trueg(y)將不會執(zhí)行誓斥。

整形(Intergers)

int/uint:多種大小的有符號整數(shù)或者無符號整數(shù)只洒。關(guān)鍵詞uint8unint256每8位為一步(無符號從8位到256位)和int8int256。特別的劳坑,uintintuint256int256的別名毕谴。

操作符:

  • 比較符:<=,<,==,!=,>=,>(返回值是bool類型)
  • 位操作符: &,|,^(按位或),~(按位非)
  • 算術(shù)符:+,-,一元-,一元+*,/,%(求余),**(?exponentiation),<<(左移),>>(右移動)

除法通常是會被截斷的(它只是編譯成EVM的DIV操作碼)涝开,但是如果兩個操作數(shù)都是字面量或者字面表達式.(?Division always truncates (it just is compiled to the DIV opcode of the EVM), but it does not truncate if both operators are literals (or literal expressions).)
被0除或者對0取模會拋出運行時異常循帐。
移位操作符的返回值的類型通常是左邊操作數(shù)的類型。表達式x<<y等價于x * 2**y,x>>y等價于x / 2**y舀武。這意味著負數(shù)的移位符號位會不變(ps:-2 << 2 = -8)拄养。目前移動負數(shù)位將會拋出運行時異常。(ps:2<<-2就會異常)

警告:Solidity負數(shù)的向右移位和其他的語言是不一樣的银舱。在Solidity中瘪匿,右移對應的是除法,所以右移負數(shù)的結(jié)果趨向于0(被截斷了)寻馏。其他語言中棋弥,右移負數(shù)的值是除法不截斷(趨向于負無窮大)(?In other programming languages the shift right of negative values works like division with rounding down (towards negative infinity).)

地址類(Address)

address是一個20字節(jié)的值(也是以太坊地址的長短)诚欠。Address也有成員變量顽染,是所有合約的基礎(chǔ)。

操作符:

  • <=,<,==,!=,>=>
地址類的成員變量
  • balancetransfer

快速瀏覽轰绵,參看地址類相關(guān)信息.
可以通過地址的balance屬性查看賬號余額粉寞,可以通過transfer將以太幣發(fā)送給其他賬號。

address x = 0x123;
address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);

注意:如果x是合約地址左腔,合約代碼(更具體些:是回調(diào)函數(shù))將會于transefer函數(shù)一同執(zhí)行(這是EVM的限制仁锯,而且不能被阻止)。如果執(zhí)行過程中翔悠,gas消耗完畢了业崖,或者執(zhí)行失敗,轉(zhuǎn)賬就會被退回蓄愁,并拋出異常双炕。

  • send
    發(fā)送是于transfer相對應的。如果執(zhí)行失敗撮抓,合約并不馬上停止妇斤,拋出異常屁倔,而是會返回false.

警告:使用send會些風險:如果堆棧深度超過1024(該問題通常是調(diào)用端產(chǎn)生的)局雄,gas不足徙垫,會導致轉(zhuǎn)賬失敗藤违。所以為了使以太幣能夠安全轉(zhuǎn)賬,使用send就得檢查返回結(jié)果强霎∷涸埽或者直接使用transfer會更好:使用接受者取回錢幣的模式扁藕。(咬像?use a pattern where the recipient withdraws the money)

  • call,callcodedelegatecall

另外算撮,為了給那些沒有依附到ABI(Application Binary Interface)的合約提供接口生宛,call函數(shù)可以接受任意多個不同類型的參數(shù)。這些參數(shù)會擴展到32位肮柜,并且串聯(lián)起來陷舅。但是第一個參數(shù)是編碼為4個字節(jié)的。审洞?In this case, it is not padded to allow the use of function signatures here.

address nameReg = 0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2;
nameReg.call("register", "MyName");
nameReg.call(bytes4(keccak256("fun(uint256)")), a);

call函數(shù)返回一個布爾量莱睁,用來說明調(diào)用的函數(shù)是正確執(zhí)行(返回true)還是發(fā)生異常(返回false)。所以我們不可能得到真實的數(shù)據(jù)返回芒澜。(為了得到真實數(shù)據(jù)缩赛,我們需要了解編碼和大小。撰糠?for this we would need to know the encoding and size in advance)

一個類似的方法,函數(shù)delegatecallcall不同的是辩昆,只是用了指定地址的地址碼阅酪,其他的參數(shù)(storage,余額等)用的都是當前合約的汁针。使用delegatecall的目的是使用保存在其他合約中的庫术辐。用戶必須確認確認兩個合約的storage必須合適才行。在homestead版本之前施无,只有一個callcode參數(shù)可用辉词,但是不能獲取到msg.sendermsg.value.

所有這三個參數(shù)call,delegatecallcallcode都是非常低級的函數(shù),并且只在迫不得已的情況下使用猾骡。因為他們會危害Solidity的類型安全瑞躺。

以上三個函數(shù)都可以使用.gas()函數(shù),但是delegatecall并不支持.value()函數(shù)兴想。
注意:所有合約都繼承了address的成員變量幢哨,所以可以通過this.balance來訪問當前合約的余額
警告:這些函數(shù)都是底層函數(shù),并小心使用嫂便。任意未知的合約可能是惡意的捞镰。如果你調(diào)用它,你就把控制權(quán)交給了它毙替,它可以反過來再調(diào)用你岸售。所以執(zhí)行完畢之后,你的變量可能已經(jīng)發(fā)生改變了厂画。

固定大小的字節(jié)數(shù)組

bytes1,bytes2,bytes3,...bytes32凸丸。bytebytes1的別名。

操作:

  • 比較:<=,<,==,!=,>=,>(返回bool
  • 位操作:&,|,^(按位異或),~(按位取反),<<(左移),>>(右移)
  • 索引:如果x的類型是bytesI袱院,那么x[k]中甲雅,有0 <= k <= I解孙,返回第k位(只讀)。

移位操作符的右操作數(shù)可以是任意整數(shù)類型(但是返回做操作數(shù)的類型)抛人。右操作數(shù)表示的是移位的個數(shù)弛姜。如果右操作數(shù)是負數(shù),那么會引起運行時異常妖枚。

成員變量:

  • .length 返回字節(jié)數(shù)組的長度(只讀)
動態(tài)大小的字節(jié)數(shù)組

bytes:動態(tài)大小的字節(jié)數(shù)組廷臼,參看Arrays,不是一種值類型。
string:動態(tài)大小的UTF-8編碼的字符串绝页,參看Arrays,不是一種值類型荠商。

根據(jù)經(jīng)驗來說,如果需要用到任意長度的字節(jié)數(shù)據(jù)续誉,那么使用bytes莱没;如果需要用到任意長度的字符串(UTF-8)數(shù)據(jù),那么使用string.如果你可以確定長度酷鸦,那么使用bytes1bytes32饰躲,因為它們會便宜些。

固定長度的數(shù)字

//todo 待完善

地址字面量

通過傳遞地址檢驗和測試的十六進制字面量是address類型臼隔,例如0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF嘹裂。39位到41位長度的十六進制字面量,沒有通過檢驗和測試的會產(chǎn)生警告摔握,并且作為平常的有理數(shù)字面量寄狼。

有理數(shù)整形字面量

整形變量是由0-9組成的一串數(shù)字。它們是十進制的氨淌。例如69就是69泊愧。Solidity中并沒有八進制,以0開頭的是不合法的盛正。

十進制小數(shù)拼卵,以.隔開,左右必須至少有一個字符蛮艰,例如1.,.11.3腋腮。
科學表示法也是支持的,基數(shù)可以是小數(shù)壤蚜,但是指數(shù)不可以即寡。例如2e10,-2e10,2e-102.5e1袜刷。

數(shù)字字面量表達式保持精確的值聪富。直到類型變?yōu)榉亲置媪款愋停ɡ绾头亲置媪康闹狄黄鹗褂茫_@意味著數(shù)字字面量的計算不會溢出或者除法的時候不會截斷著蟹。

例如墩蔓,(2**800 + 1) - 2**800的結(jié)果是常量1(類型是uint8類型)梢莽。盡管中間值超過了機器字的大小。(奸披? although intermediate results would not even fit the machine word size)另外昏名,.5*8的結(jié)果是4(因為表達式中沒有用到非整數(shù))。

如果結(jié)果不是整數(shù)阵面,會選擇合適的ufixed或者fixed類型來表示它們轻局。大小會計算出足夠它們使用的大小。(大概有理數(shù)中最差的情況样刷。)

在表達式var x = 1/4;中仑扑,x會使用ufixed0x8類型,但是在var x = 1/3中置鼻,將會使用ufixed0x256類型镇饮。因為1/3在二進制中無法精確表示。所以會是一個精確值箕母。

可以用在整數(shù)中的任何操作符储藐,也可以用在數(shù)字字面量表達式中,只要操作數(shù)是整數(shù)司蔬。如果有操作數(shù)是小數(shù),位操作符是不可以使用的姨蝴。指數(shù)如果是小數(shù)俊啼,指數(shù)運算也是不允許的。(因為會產(chǎn)生物理數(shù))左医。

注意:Solidity對于每種有理數(shù)授帕,都有一個數(shù)字字面量類型。整數(shù)字面量和有理數(shù)字面量都屬于數(shù)字字面量類型浮梢。另外跛十,所有的數(shù)字字面量表達式(例如只包含數(shù)字字面量和操作符的表達式)都是數(shù)字字面量類型。所以數(shù)字字面量表達式1+22+1都是屬于相同的數(shù)字字面量類型——有理數(shù)——3

注意:很多有盡十進制小數(shù)秕硝,如5.3743芥映,在二進制表示法中是無盡的。5.3743的正確類型是ufixed8x248因為這樣可以更加精確的表示該數(shù)远豺。如果你想用其他類型奈偏,如ufixed(像ufixed128x128),你必須要指明期望的精度:x + ufixed(5.3743)

警告:在早期版本中躯护,整數(shù)字面量的除法會有截斷惊来。但是現(xiàn)在會轉(zhuǎn)換為有理數(shù)。例如5 / 2并不等于2,而是2.5

注意:當數(shù)字字面量表達式與非字面量類型一起使用的時候棺滞,會轉(zhuǎn)變?yōu)榉亲置媪款愋筒靡稀T谌缦滤镜睦又惺冈ǎM管我們知道賦給b的值是整形,但是在表達式中間枉证,仍然使用固定大小的浮點類型(非有理數(shù)字面量)矮男,所以代碼不會編譯。

uint128 a = 1;
uint128 b = 2.5 + a + 0.5;
字符串字面量

字符串字面量由單引號或者雙引號包裹(“foo”或者'bar')刽严。?They do not imply trailing zeroes as in C; "foo"代表3個字節(jié)昂灵,不是四個。舞萄?As with integer literals, their type can vary, but they are implicitly convertible to bytes1, ..., bytes32, if they fit, to bytes and to string.
字符串字面量支持換碼符(escape characters)眨补,例如\n\xNN\uNNNN倒脓。\xNN使用十六進制的值撑螺,并插入合適的字節(jié)數(shù)據(jù)(?\xNN takes a hex value and inserts the appropriate byte,)崎弃。\uNNNN使用的是unicode碼甘晤,插入的是UTF-8的字符串。(饲做?while \uNNNN takes a Unicode codepoint and inserts an UTF-8 sequence)

十六進制字面量

十六進制數(shù)據(jù)以hex關(guān)鍵字打頭线婚,并且通過雙引號或者單引號包裹(hex"001122FF")。該字面量內(nèi)容必須是十六進制數(shù)據(jù)的字符串盆均,值必須是代表這些字符的二進制數(shù)據(jù)塞弊。(?Their content must be a hexadecimal string and their value will be the binary representation of those values.)
十六進制字面量有點像字符串字面量泪姨,并且也有一些轉(zhuǎn)換限制游沿。

枚舉類型

枚舉類型為用戶提過了一種在Solidity中自定義類型的方法。他們可以方便的轉(zhuǎn)換為整形肮砾,或者從整形轉(zhuǎn)換而來诀黍。但是隱式變換是不允許的。顯式變換在代碼執(zhí)行的時候檢查值的范圍仗处,執(zhí)行失敗會產(chǎn)生一個異常眯勾。枚舉類型至少需要一個數(shù)字。

pragma solidity ^0.4.0;

contract test {
    enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
    ActionChoices choice;
    ActionChoices constant defaultChoice = ActionChoices.GoStraight;

    function setGoStraight() {
        choice = ActionChoices.GoStraight;
    }

    // 由于枚舉類型不是ABI的一部分婆誓。getChoice函數(shù)會自動的變換為“getChoice() returns (uint8)”咒精。整數(shù)類型的大小為能夠保存所有enum值的大小。例如:如果你有更大的數(shù)值旷档,就會使用uint16等模叙。
    function getChoice() returns (ActionChoices) {
        return choice;
    }

    function getDefaultChoice() returns (uint) {
        return uint(defaultChoice);
    }
}
函數(shù)類型

函數(shù)類型是函數(shù)的類型。函數(shù)類型的變量可以通過函數(shù)賦值鞋屈,函數(shù)類型的參數(shù)可以傳遞給函數(shù)范咨。函數(shù)也可以通過函數(shù)返回故觅。函數(shù)類型分為兩種-內(nèi)部函數(shù)和外部函數(shù)。
內(nèi)部函數(shù)只能在當前合約的內(nèi)部使用(更加準確的說渠啊,是當前代碼單元里输吏,代碼單元中包含內(nèi)部庫函數(shù)和繼承函數(shù)),因為他們不能在合約外部使用替蛉。調(diào)用內(nèi)部函數(shù)就像跳轉(zhuǎn)到函數(shù)的標簽處贯溅,就像在合約內(nèi)部調(diào)用函數(shù)。(躲查?Calling an internal function is realized by jumping to its entry label, just like when calling a function of the current contract internally.)
外部函數(shù)由地址和函數(shù)簽名組成它浅。他們可以從外部函數(shù)調(diào)用中被傳遞或者返回。(镣煮?External functions consist of an address and a function signature and they can be passed via and returned from external function calls.)
函數(shù)類型的格式如下:

function (<parameter types>) {internal|external} [constant] [payable] [returns (<return types>)]

與參數(shù)類型做對比姐霍,返回類型不能為空,如果函數(shù)沒有返回典唇,那么returns (<return types>)都應該刪除镊折。
默認情況下,函數(shù)類型是內(nèi)部的介衔,所以internal關(guān)鍵字可以刪除恨胚。
在當前合約中可以由兩種方法調(diào)用函數(shù):要么直接通過函數(shù)名稱,f或者使用this.f炎咖。第一種是內(nèi)部函數(shù)調(diào)用赃泡,后一種是外部函數(shù)。
如果函數(shù)類型變量沒有初始化塘装,那么調(diào)用就會引發(fā)異常急迂。如果你對函數(shù)delete之后影所,再調(diào)用蹦肴,同樣會引發(fā)這個錯誤。
如果外部函數(shù)類型在Solidity外部調(diào)用猴娩,他們會被當作function類型阴幌,該類型將地址和函數(shù)識別碼一同以bytes24類型編碼。

需要注意的是卷中,當前合約的公有函數(shù)既可以用作內(nèi)部函數(shù)矛双,也可以用作外部函數(shù)。如果想用作內(nèi)部函數(shù)蟆豫,那么直接通過f(函數(shù)名稱)來調(diào)用议忽,或者如果想用作外部函數(shù),通過this.f來調(diào)用十减。

以下的例子顯示了如何使用內(nèi)部函數(shù)類型:

pragma solidity ^0.4.5;

library ArrayUtils {
  // 內(nèi)部函數(shù)可以在內(nèi)部庫內(nèi)部使用栈幸,因為他們的執(zhí)行上下文一致
  function map(uint[] memory self, function (uint) returns (uint) f)
    internal
    returns (uint[] memory r)
  {
    r = new uint[](self.length);
    for (uint i = 0; i < self.length; i++) {
      r[i] = f(self[i]);
    }
  }
  function reduce(
    uint[] memory self,
    function (uint, uint) returns (uint) f
  )
    internal
    returns (uint)
  {
    r = self[0];
    for (uint i = 1; i < self.length; i++) {
      r = f(r, self[i]);
    }
  }
  function range(uint length) internal returns (uint[] memory r) {
    r = new uint[](length);
    for (uint i = 0; i < r.length; i++) {
      r[i] = i;
    }
  }
}

contract Pyramid {
  using ArrayUtils for *;
  function pyramid(uint l) returns (uint) {
    return ArrayUtils.range(l).map(square).reduce(sum);
  }
  function square(uint x) internal returns (uint) {
    return x * x;
  }
  function sum(uint x, uint y) internal returns (uint) {
    return x + y;
  }
}

如下例子愤估,說明如何使用外部函數(shù):

pragma solidity ^0.4.11;

contract Oracle {
  struct Request {
    bytes data;
    function(bytes memory) external callback;
  }
  Request[] requests;
  event NewRequest(uint);
  function query(bytes data, function(bytes memory) external callback) {
    requests.push(Request(data, callback));
    NewRequest(requests.length - 1);
  }
  function reply(uint requestID, bytes response) {
    // 這里需要確認返回的數(shù)據(jù)來自受信任的源
    requests[requestID].callback(response);
  }
}

contract OracleUser {
  Oracle constant oracle = Oracle(0x1234567); // 已知合約
  function buySomething() {
    oracle.query("USD", this.oracleResponse);
  }
  function oracleResponse(bytes response) {
    require(msg.sender == address(oracle));
    // 使用數(shù)據(jù)
  }
}

lambda表達式或者行內(nèi)函數(shù)還在計劃開發(fā)進程中,尚未支持速址。

引用類型

復雜的類型玩焰,例如,某種類型芍锚,總是不能很好的適應256位大小昔园,相比較我們之前的類型,需要更加小心處理并炮。因為拷貝它們可能會產(chǎn)生很大的花費默刚。我們必須考慮將它們保存在內(nèi)存(非連續(xù)的)中還是在storage(狀態(tài)變量保存的地方)中。

數(shù)據(jù)位置(Data location)

每種復雜的類型渣触,例如數(shù)組和結(jié)構(gòu)體羡棵,有一種另外的注釋,數(shù)據(jù)位置(data location)嗅钻,關(guān)于是否保存在內(nèi)存中皂冰,還是在Storage里。根據(jù)上下文养篓,會有個默認的類型秃流。但是我們也可以通過添加storage或者memory關(guān)鍵字來改變這個類型。函數(shù)的參數(shù)(包括返回值)都是保存在memory中的柳弄,本地變量舶胀,狀態(tài)變量默認是storage
還有第三種數(shù)據(jù)位置碧注,“calldata”嚣伐,這是一個保存函數(shù)參數(shù)的非連續(xù)區(qū),不可修改區(qū)萍丐。外部函數(shù)的參數(shù)(不包含返回參數(shù))強制的保存在"calldata"中,和memory差不多轩端。
理解數(shù)據(jù)位置是非常重要的,因為它改變了賦值的行為:storage和內(nèi)存間的賦值逝变,以及賦值給轉(zhuǎn)臺變量(或者從其他變量賦值)總是會生成一個獨立的拷貝基茵。賦值給本地變量只是賦值了一個引用。并且這個引用總是指向狀態(tài)變量壳影,即使這個狀態(tài)變量在程序運行期間發(fā)生了變化拱层。換句話說,在內(nèi)存數(shù)據(jù)之間的賦值不會生成一個數(shù)據(jù)拷貝宴咧。

pragma solidity ^0.4.0;

contract C {
    uint[] x; // 該數(shù)據(jù)保存在storage中

    // memoryArray的數(shù)據(jù)保存在memory中
    function f(uint[] memoryArray) {
        x = memoryArray; // 沒毛病根灯,將整個數(shù)組保存到storage中
        var y = x; // 沒毛病,給指針賦值,y的數(shù)據(jù)位置為storage
        y[7]; // 可以的烙肺,返回第8個元素
        y.length = 2; // 可以的芥驳,通過y來改變x
        delete x; // 可以的,清空array茬高,并且也改變了y
        // 下面的語句是錯誤的兆旬,本該在storage中生成一個暫時的,沒有命名的Array /
        // 但是storage已經(jīng)靜態(tài)的被分配了:
        // y = memoryArray;
        // 以下語句也是錯誤的怎栽,因為它會重置指針丽猬,但是它并沒有實質(zhì)的指向。
        // delete y;
        g(x); // 調(diào)用g函數(shù)熏瞄,參數(shù)為x的引用
        h(x); // 調(diào)用h函數(shù)脚祟,并且在memory中生成一個單獨的,暫時的强饮,x的拷貝
    }

    function g(uint[] storage storageArray) internal {}
    function h(uint[] memoryArray) {}
}
總結(jié)

強制數(shù)據(jù)位置:

  • 外部函數(shù)的參數(shù)(不包括返回值):calldata
  • 狀態(tài)變量:storage

默認數(shù)據(jù)位置:

  • 函數(shù)的參數(shù)(包括返回值):內(nèi)存
  • 所有其他本地變量:storage
數(shù)組

數(shù)組可以在編譯的時候確定其長度由桌,或者它們可以動態(tài)變化。對于storage數(shù)組邮丰,元素類型可以是任意的(例如行您,其他數(shù)組,映射或者結(jié)構(gòu)體)剪廉;對于內(nèi)存數(shù)組娃循,它不能成為映射,而且如果它是公有函數(shù)的參數(shù)斗蒋,那么只能是ABI類型捌斧。
一個具有k長度的,元素類型為T的數(shù)組表示為T[k]泉沾,動態(tài)長度的數(shù)組為T[]捞蚂。例如,一個包含有五個動態(tài)數(shù)組的數(shù)組跷究,可以表示為uint[][5](注意這里和其他的語言的記法相反)姓迅。獲取到第三個數(shù)組中的第二個元素,可以用x[2][1](索引從0開始揭朝,這里獲取數(shù)據(jù)和定義是兩種相反的方式队贱,如x[2] shaves off one level in the type from the right)
bytesstring是特殊的數(shù)組色冀。bytes就相當于是byte[]潭袱,但是它位于calldata,而且比較緊湊(锋恬?but it is packed tightly in calldata)屯换。string相當于是bytes,但是目前為止不能修改長度和字符索引。
所以bytesbyte[]相比彤悔,會優(yōu)先選擇byte[]嘉抓,因為更加便宜。
注意:如果你想要獲取string的byte表達晕窑,可以使用bytes(s).length/bytes(s)[7] = x;抑片。你要注意的是,你訪問的是UTF-8的低級字節(jié)表達杨赤,不是單獨的一個字符敞斋。
數(shù)組也可以是public的,solidity會生成它的getter函數(shù)疾牲。調(diào)用getter函數(shù)必須要有一個索引參數(shù)植捎。

分配內(nèi)存數(shù)組

在內(nèi)存中創(chuàng)建一個可變長度的數(shù)組可以通過new關(guān)鍵字來實現(xiàn)。為了與storage數(shù)組相抵制阳柔,內(nèi)存數(shù)組不能通過改變.length來改變長度焰枢。

pragma solidity ^0.4.0;

contract C {
    function f(uint len) {
        uint[] memory a = new uint[](7);
        bytes memory b = new bytes(len);
        // 這里,a.length == 7舌剂,b.length == len
        a[6] = 8;
    }
}
數(shù)組字面量/線性數(shù)組

數(shù)組字面量济锄,是寫作表達式一樣的數(shù)組,它不是馬上被賦值到一個變量中霍转。

pragma solidity ^0.4.0;

contract C {
    function f() {
        g([uint(1), 2, 3]);
    }
    function g(uint[3] _data) {
        // ...
    }
}

數(shù)組字面量的類型是固定長度的內(nèi)存數(shù)組拟淮,元素類型為給出元素的公共類型。[1,2,3]的類型是uint[8] memory谴忧,因為所有元素的類型是uint8很泊。正因為如此,在上面例子中沾谓,第一個元素就很有必要轉(zhuǎn)換為uint委造。需要注意的是,當前固定長度的內(nèi)存數(shù)組不能賦值給可變長度的內(nèi)存數(shù)組均驶,所以如下的代碼是不可行的:

program solidity ^0.4.0

contract C {
    function f() {
        // 
        uint[] x = [uint(1),3,4];
    }
}

未來會把這個限制移除昏兆,但是現(xiàn)在有些難題尚未解決,比如如何在ABI中傳遞數(shù)組妇穴。

成員變量

length:
數(shù)組有一個length成員變量來指示數(shù)組的長度爬虱。動態(tài)長度數(shù)組保存在storage中(不是在內(nèi)存中),能夠動態(tài)變化長度腾它。程序不會自動的訪問長度以外的元素跑筝。內(nèi)存數(shù)組一旦創(chuàng)建,它的長度是固定的(但是是動態(tài)的瞒滴,例如可以在運行的時候決定長度)

push:
動態(tài)storage數(shù)組和bytes(不是string)有一個成員函數(shù)曲梗,稱為push赞警,可以用來在數(shù)組末尾添加元素。該函數(shù)返回新的長度虏两。

警告:在外部函數(shù)中還不能使用二維數(shù)組

**警告:由于EVM的限制愧旦,不能在外部函數(shù)調(diào)用中返回動態(tài)內(nèi)容。在contract C { function f() returns (uint[]) { ... } }中的函數(shù)f定罢,如果在web3中調(diào)用笤虫,將會返回數(shù)據(jù),但是在solidity中調(diào)用祖凫,不會返回數(shù)據(jù)耕皮。
現(xiàn)在一個變通的方法是返回一個靜態(tài)的足夠大的數(shù)組。
**

pragma solidity ^0.4.0;

contract ArrayContract {
    uint[2**20] m_aLotOfIntegers;
    // 注意這里定義的不是兩個動態(tài)數(shù)組蝙场,而是一個動態(tài)數(shù)組凌停,其元素為長度為2的數(shù)組。
    bool[2][] m_pairsOfFlags;
    
    // newPairs會保存在memory中 - 函數(shù)參數(shù)的默認類型售滤。
    function setAllFlagPairs(bool[2][] newPairs) {
        // 賦值給storage數(shù)組罚拟,替換整個數(shù)組。
        m_pairsOfFlags = newPairs;
    }

    function setFlagPair(uint index, bool flagA, bool flagB) {
        // 越界訪問將會產(chǎn)生異常
        m_pairsOfFlags[index][0] = flagA;
        m_pairsOfFlags[index][1] = flagB;
    }

    function changeFlagArraySize(uint newSize) {
        // 如果數(shù)組的長度變小了完箩,那么多余的元素將會被刪除
        m_pairsOfFlags.length = newSize;
    }

    function clear() {
        // 清空數(shù)組
        delete m_pairsOfFlags;
        delete m_aLotOfIntegers;
        // 清空數(shù)組
        m_pairsOfFlags.length = 0;
    }

    bytes m_byteData;

    function byteArrays(bytes data) {
        // byte 數(shù)組 ("bytes") 是不同的赐俗,因為它們保存時沒有保留空余的空間
        // 但是以"uint8[]"的類型來對待
        // ?byte arrays ("bytes") are different as they are stored without padding,
        // ?but can be treated identical to "uint8[]"
        m_byteData = data;
        m_byteData.length += 7;
        m_byteData[3] = 8;
        delete m_byteData[2];
    }

    function addFlag(bool[2] flag) returns (uint) {
        return m_pairsOfFlags.push(flag);
    }

    function createMemoryArray(uint size) returns (bytes) {
        // 動態(tài)的內(nèi)存數(shù)組通過new關(guān)鍵字生成。
        uint[2][] memory arrayOfPairs = new uint[2][](size);
        // 生成一個動態(tài)byte數(shù)組
        bytes memory b = new bytes(200);
        for (uint i = 0; i < b.length; i++)
            b[i] = byte(i);
        return b;
    }
}
結(jié)構(gòu)體

Solidity提供了定義類型的方法:通過結(jié)構(gòu)體弊知。例子如下所示:

pragma solidity ^0.4.11;

contract CrowdFunding {
    // 定義新的類型阻逮,包含兩個數(shù)據(jù)字段
    struct Funder {
        address addr;
        uint amount;
    }

    struct Campaign {
        address beneficiary;
        uint fundingGoal;
        uint numFunders;
        uint amount;
        mapping (uint => Funder) funders;
    }

    uint numCampaigns;
    mapping (uint => Campaign) campaigns;

    function newCampaign(address beneficiary, uint goal) returns (uint campaignID) {
        campaignID = numCampaigns++; // campaignID 是返回值變量
        // 生成一個新的結(jié)構(gòu)體數(shù)據(jù),并保存在storage中秩彤。 We leave out the mapping type.
        campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);
    }

    function contribute(uint campaignID) payable {
        Campaign c = campaigns[campaignID];
        // 生成一個新的內(nèi)存數(shù)據(jù)叔扼。通過給定的值進行初始化,并拷貝到storage中漫雷。
        // 你也可以通過 Funder(msg.sender, msg.value) 進行初始化.
        c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value});
        c.amount += msg.value;
    }

    function checkGoalReached(uint campaignID) returns (bool reached) {
        Campaign c = campaigns[campaignID];
        if (c.amount < c.fundingGoal)
            return false;
        uint amount = c.amount;
        c.amount = 0;
        c.beneficiary.transfer(amount);
        return true;
    }
}

合約并不提供眾籌合約的所有函數(shù)瓜富,但是它包含了理解結(jié)構(gòu)體的基本概念。結(jié)構(gòu)體類型可以用在數(shù)組和映射中降盹,并且它本身可以有映射和數(shù)組与柑。

結(jié)構(gòu)體類型包含本身的類型的字段是不可以的,盡管這個結(jié)構(gòu)體是映射的值類型(蓄坏?although the struct itself can be the value type of a mapping member.)价捧。這個限制是很有必要的藐吮,因為結(jié)構(gòu)體的大小應該有限遗增。
要注意在所有函數(shù)中,結(jié)構(gòu)體類型是怎樣賦值給本地變量的(默認是storage數(shù)據(jù)位置)涕俗。它并沒有復制結(jié)構(gòu)體妹蔽,而是保存了他的引用椎眯,所以通過本地變量,直接給所引用的結(jié)構(gòu)體成員賦值也會改變數(shù)據(jù)胳岂。

當然编整,你也可以直接訪問結(jié)構(gòu)體的字段,不需要將它賦值給一個本地變量乳丰,例如campaigns[campaignID].amount = 0.

映射

映射類型通過這樣的形式定義:mapping(_KeyType => _ValueType)掌测。這里_KeyType可以是除了映射,動態(tài)長度的數(shù)組产园,合約汞斧,枚舉和結(jié)構(gòu)體之外的任何類型。_ValueType可以是所以的類型什燕,包括映射粘勒。
映射可以看成是一個hash表,這個表的所有可能的鍵初始化為byte表示全為0的值:這個是默認值.相似的:鍵數(shù)據(jù)不是存儲在映射里屎即,而是鍵的keccak256hash庙睡,用來查找對應的值。
所以映射沒有長度或者鍵值的概念技俐,也不是集合乘陪。(?Because of this, mappings do not have a length or a concept of a key or value being “set”.)
映射只允許狀態(tài)變量(或者在內(nèi)部函數(shù)中的storage引用類型)雕擂。
可以設置映射為public啡邑,solidity會自動生成getter。_KeyType是getter函數(shù)必須的參數(shù)井赌,返回值為_ValueType谤逼。
_ValueType值同樣可以是映射,會遞歸產(chǎn)生getter函數(shù)仇穗。(森缠?The _ValueType can be a mapping too. The getter will have one parameter for each _KeyType, recursively.)

pragma solidity ^0.4.0;

contract MappingExample {
    mapping(address => uint) public balances;

    function update(uint newBalance) {
        balances[msg.sender] = newBalance;
    }
}

contract MappingUser {
    function f() returns (uint) {
        return MappingExample(<address>).balances(this);
    }
}

注意:映射不可以遍歷,但是可以在此基礎(chǔ)上生成新的結(jié)構(gòu)仪缸。例如贵涵,可遍歷映射

使用LValues的操作符

如果a是一個LValue(例如,一個變量或者是可以被賦值something)恰画,那么可以使用以下的操作符宾茂。
a += e是和a = a + e一樣的。操作符-=拴还,*=跨晴,/=%=片林,|=端盆,&=^=也是對應的怀骤。a++a--分別對應于a += 1a -= 1。但是表達式本身還是保持a的原始值焕妙。相反的蒋伦,--a++a同樣是對a加上1,但是返回了改變后的值焚鹊。

刪除

delete a將a類型的初始值賦給a痕届。例如,對于整形末患,這個相當于是a = 0研叫。它也可以用于數(shù)組,將一個長度為0的動態(tài)長度數(shù)組賦值給變量璧针,或者是一個與之具有相通長度的靜態(tài)數(shù)組嚷炉,但是所有元素都被初始化的數(shù)組。對于結(jié)構(gòu)體探橱,將初始化所有的字段渤昌。
delete對整個映射是無效的(因為key可以是任何值,而且通常我們都不知道)走搁。所以独柑,如果是delete結(jié)構(gòu)體,它將會重置所有非映射類型的字段私植,并且將遞歸字段忌栅,直到遇上映射。
delete a其實是對a重新賦值曲稼,例如重新賦值為一個對象索绪。

pragma solidity ^0.4.0;

contract DeleteExample {
    uint data;
    uint[] dataArray;

    function f() {
        uint x = data;
        delete x; // 將x設置為0,并不影響其他數(shù)據(jù)
        delete data; //將data設置,不會影響到x贫悄,因為x保存的是值的拷貝
        uint[] y = dataArray;
        delete dataArray; // 將dataArray.length設置為0, 但是因為uint[]是一個復雜的對象瑞驱,所以y的值也被影響,因為y是storage對象的引用窄坦。
        // 另一方面: "delete y"是無效的唤反,因為引用一個storage對象必須要有一個storage對象。
    }
}

初級類型的轉(zhuǎn)換

隱式的轉(zhuǎn)換

如果操作符被用于不同的類型時鸭津,編譯器會嘗試對一個操作數(shù)進行隱式的轉(zhuǎn)換成另一個操作數(shù)的類型(賦值的時候也是一樣的)。一般情況下盏阶,如果語意上有意義,并且沒有信息損失闷袒,值類型之間的隱式轉(zhuǎn)換是可行的霜运。uint8可以轉(zhuǎn)換為uint16蒋腮,int128可以轉(zhuǎn)換為int256,但是int8不能轉(zhuǎn)換為uint256(因為uint256不能存儲負數(shù))。另外,無符號整形可以轉(zhuǎn)換為同樣大小的或者更大的bytes竭讳,但是反過來不行洛波。任何可以轉(zhuǎn)換為uint160的類型可以轉(zhuǎn)換為address.

顯式的轉(zhuǎn)換

如果編譯器不允許隱式轉(zhuǎn)換胰舆,但是你知道你要怎么做扽時候倦零,可以用顯式轉(zhuǎn)換诞帐。需要注意的是愕鼓,顯式轉(zhuǎn)換可能會導致有不期望的結(jié)果磺送,所以要充分測試估灿,并且保證結(jié)果是你期望的抵窒。下面的例子說明了從負數(shù)的int8轉(zhuǎn)換為uint類型:

int8 y = -3;
uint x = uint(y);

這個代碼端最后疙赠,x的值變?yōu)?code>0xfffff..fd(64個十六進制字符),值為-3付材。

如果將類型顯式轉(zhuǎn)換為更小的類型,那么高位會被裁切掉:

uint32 a = 0x12345678;
uint16 b = uint16(a); // 現(xiàn)在b的值為0x5678

類型推斷

簡便起見圃阳,并不是總是需要為變量顯式的指定類型厌衔,編譯器會自動的對第一個表達式的類型進行引用,并賦值:

uint24 x = 0x123;
var y = x;

這里捍岳,y的類型將會是uint24富寿。函數(shù)參數(shù)或者返回值不能用var.
警告:類型只能從第一個表達式推斷,所以下面的例子是個死循環(huán)锣夹。因為i的類型為uint8页徐,該類型的所有值都是小于2000的。for (var i = 0; i < 2000; i++) { ... }

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末银萍,一起剝皮案震驚了整個濱河市变勇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖搀绣,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件飞袋,死亡現(xiàn)場離奇詭異,居然都是意外死亡链患,警方通過查閱死者的電腦和手機巧鸭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來麻捻,“玉大人纲仍,你說我怎么就攤上這事∶潮希” “怎么了郑叠?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長崖咨。 經(jīng)常有香客問我锻拘,道長油吭,這世上最難降的妖魔是什么击蹲? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮婉宰,結(jié)果婚禮上歌豺,老公的妹妹穿的比我還像新娘。我一直安慰自己心包,他們只是感情好类咧,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蟹腾,像睡著了一般痕惋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上娃殖,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天值戳,我揣著相機與錄音,去河邊找鬼炉爆。 笑死堕虹,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的芬首。 我是一名探鬼主播赴捞,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼郁稍!你這毒婦竟也來了赦政?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤耀怜,失蹤者是張志新(化名)和其女友劉穎恢着,沒想到半個月后掸屡,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡然评,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年仅财,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片碗淌。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡盏求,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出亿眠,到底是詐尸還是另有隱情碎罚,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布纳像,位于F島的核電站荆烈,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏竟趾。R本人自食惡果不足惜憔购,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望岔帽。 院中可真熱鬧玫鸟,春花似錦、人聲如沸犀勒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贾费。三九已至钦购,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間褂萧,已是汗流浹背押桃。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留箱玷,地道東北人怨规。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像锡足,于是被迫代替她去往敵國和親波丰。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 本章內(nèi)容 使用對象 創(chuàng)建并操作數(shù)組 理解基本的 JavaScript 類型 使用基本類型和基本包裝類型 引用類型的...
    悶油瓶小張閱讀 677評論 0 0
  • 引用類型的值時引用類型的一個實例舶得。在ECMAScript中掰烟,引用類型是一種數(shù)據(jù)結(jié)構(gòu),用于將數(shù)據(jù)和功能組織在一起。有...
    cooore閱讀 280評論 0 1
  • 愛我的母親, 你們亦是如此先馆。 今天我想給大家分享一些我和我母親的故事发框。 媽,以后我賺錢養(yǎng)家煤墙,你負責貌美如花梅惯。 她,...
    隨心小芝閱讀 314評論 2 2
  • 詩歌/夕木夏睿 你悄悄地來到了 就像一陣風吹過我的世界 你悄悄地有走了 就像暴風驟雨一樣及時 就讓狂風吹亂我頭發(fā)仿野,...
    晨立夏塵閱讀 242評論 0 0
  • 上帝為她關(guān)上一扇門铣减,一定會為她打開一扇窗。 1. “媽媽脚作,為什么我明明看得見葫哗,他們還要笑我是瞎子?” 剛上幼兒園的...
    梔子茗香閱讀 690評論 22 12