除了 switch和goto,solidity的絕大多數(shù)控制結(jié)構(gòu)均來自于C / JavaScript,if, else, while, for, break, continue, return, ? :, 的語義均和C / JavaScript一樣。
條件語句中的括號不能省略,但在單條語句前后的花括號可以省略。
注意,(Solidity中 )沒有象C和JavaScrip那樣 ,從非布爾類型類型到布爾類型的轉(zhuǎn)換,? 所以if (1){…}不是合法的Solidity(語句)。
內(nèi)部函數(shù)調(diào)用
當(dāng)前合約和函數(shù)可以直接調(diào)用(“內(nèi)部”),也可以遞歸, 所以你會看到在這個(gè)“荒謬”的例子:
contract?c {
??function?g(uint?a)?returns?(uint?ret) {?return?f(); }
??function?f()?returns?(uint?ret) {?return?g(7)?+?f(); }}
這些函數(shù)調(diào)用在EMV里翻譯成簡單的jumps 語句。當(dāng)前內(nèi)存沒有被清除,? 即通過內(nèi)存引用的函數(shù)是非常高效的。僅僅同一個(gè)合約的函數(shù)可在內(nèi)部被調(diào)用。
外部函數(shù)調(diào)用
表達(dá)式this.g(8);也是一個(gè)合法的函數(shù)調(diào)用,? 但是這一次,這個(gè)函數(shù)將被稱為“外部”的, 通過消息調(diào)用,而不是直接通過jumps調(diào)用。其他合約的函數(shù)也是外部調(diào)用。對于外部調(diào)用,所有函數(shù)參數(shù)必須被復(fù)制到內(nèi)存中。
當(dāng)調(diào)用其他合約的函數(shù)時(shí), 這個(gè)調(diào)用的wei的數(shù)量被發(fā)送和gas被定義:
contract?InfoFeed {
??function?info()?returns?(uint?ret) {?return?42; }
}
contract?Consumer {
? InfoFeed feed;
??function?setFeed(address?addr) { feed?=?InfoFeed(addr); }
??function?callFeed() { feed.info.value(10).gas(800)(); }
}
注意:表達(dá)式InfoFeed(addr)執(zhí)行顯式的類型轉(zhuǎn)換, 意思是“我們知道給定的地址的合約類型是InfoFeed”, 這并不執(zhí)行構(gòu)造函數(shù)。 我們也可以直接使用函數(shù)setFeed(InfoFeed?_feed) { feed = _feed; }。 注意: feed.info.value(10).gas(800)是(本地)設(shè)置值和函數(shù)調(diào)用發(fā)送的gas數(shù)量, 只有最后的括號結(jié)束后才完成實(shí)際的調(diào)用。
有參數(shù)的函數(shù)調(diào)用可以有名字,不使用參數(shù)的名字(特別是返回參數(shù))可以省略。
contract?c {
??function?f(uint?key,?uint?value) { ... }
??function?g() {
????// named arguments???具名參數(shù)
??? f({value:?2, key:?3});
? }
??// omitted parameters?省略名字的參數(shù)
??function?func(uint?k,?uint)?returns(uint) {
????return?k;
? }
}
表達(dá)式的計(jì)算順序是不確定的(準(zhǔn)確地說是, 順序表達(dá)式樹中的子節(jié)點(diǎn)表達(dá)式計(jì)算順序是不確定的的, 但他們對節(jié)點(diǎn)本身,計(jì)算表達(dá)式順序當(dāng)然是確定的)。只保證語句執(zhí)行順序,以及布爾表達(dá)式的短路規(guī)則。
析構(gòu)賦值并返回多個(gè)值
Solidity內(nèi)部允許元組類型,即一系列的不同類型的對象的大小在編譯時(shí)是一個(gè)常量。這些元組可以用來同時(shí)返回多個(gè)值,并且同時(shí)將它們分配給多個(gè)變量(或左值運(yùn)算)
contract?C {
??uint[] data;
??function?f()?returns?(uint,?bool,?uint) {
????return?(7,?true, 2);
? }
??function?g() {
????// Declares and assigns the variables. Specifying the type explicitly is not possible.
????var?(x, b, y)?=?f();
????// Assigns to a pre-existing variable.
??? (x, y)?=?(2, 7);
????// Common trick to swap values -- does not work for non-value storage types.
??? (x, y)?=?(y, x);
????// Components can be left out (also for variable declarations).
????// If the tuple ends in an empty component,
????// the rest of the values are discarded.
??? (data.length,)?=?f();?// Sets the length to 7
????// The same can be done on the left side.
??? (,data[3])?=?f();?// Sets data[3] to 2
????// Components can only be left out at the left-hand-side of assignments, with
????// one exception:
??? (x,)?=?(1,);
????// (1,) is the only way to specify a 1-component tuple, because (1) is
????// equivalent to 1.
? }
}
contract?C {
??uint[] data;
??function?f()?returns?(uint,?bool,?uint) {
????return?(7,?true, 2);
? }
??function?g() {
????// Declares and assigns the variables. Specifying the type explicitly is not possible.?聲明和賦值變量,不必顯示定義類型
????var?(x, b, y)?=?f();
????// Assigns to a pre-existing variable.?賦值給已經(jīng)存在的變量
??? (x, y)?=?(2, 7);
????// Common trick to swap values -- does not work for non-value storage types.?交換值的技巧-對非值存儲類型不起作用
??? (x, y)?=?(y, x);
????// Components can be left out (also for variable declarations).?元素可排除(對變量聲明也適用)
????// If the tuple ends in an empty component,?如果元組是以空元素為結(jié)尾
????// the rest of the values are discarded.??值的其余部分被丟棄
??? (data.length,)?=?f();?// Sets the length to 7?設(shè)定長度為7
????// The same can be done on the left side.?同樣可以在左側(cè)做
??? (,data[3])?=?f();?// Sets data[3] to 2??將data[3] 設(shè)為2
????// Components can only be left out at the left-hand-side of assignments, with
????// one exception:????組件只能在賦值的左邊被排除,有一個(gè)例外
??? (x,)?=?(1,);
????// (1,) is the only way to specify a 1-component tuple, because (1) is?????(1,)是定義一個(gè)元素的元組,(1)是等于1
????// equivalent to 1.
? }}
對于象數(shù)組和結(jié)構(gòu)體這樣的非值類型,賦值的語義更復(fù)雜些。賦值到一個(gè)狀態(tài)變量總是需要創(chuàng)建一個(gè)獨(dú)立的副本。另一方面,對基本類型來說,賦值到一個(gè)局部變量需要創(chuàng)建一個(gè)獨(dú)立的副本, 即32字節(jié)的靜態(tài)類型。如果結(jié)構(gòu)體或數(shù)組(包括字節(jié)和字符串)從狀態(tài)變量被賦值到一個(gè)局部變量,? 局部變量則保存原始狀態(tài)變量的引用。第二次賦值到局部變量不修改狀態(tài),只改變引用。賦值到局部變量的成員(或元素)將改變狀態(tài)。
有一些自動拋出異常的情況(見下文)。您可以使用throw 指令手動拋出一個(gè)異常。異常的影響是當(dāng)前執(zhí)行的調(diào)用被停止和恢復(fù)(即所有狀態(tài)和余額的變化均沒有發(fā)生)。另外, 異常也可以通過Solidity 函數(shù) “冒出來”, (一旦“異常”發(fā)生, 就send "exceptions", call和callcode底層函數(shù)就返回false)。
捕獲異常是不可能的。
在接下來的例子中,我們將演示如何輕松恢復(fù)一個(gè)Ether轉(zhuǎn)移,以及如何檢查send的返回值:
contract?Sharer {
????function?sendHalf(address?addr)?returns?(uint?balance) {
????????if?(!addr.send(msg.value/2))
????????????throw;?// also reverts the transfer to Sharer
????????return?this.balance;
??? }
}
contract?Sharer {
????function?sendHalf(address?addr)?returns?(uint?balance) {
????????if?(!addr.send(msg.value/2))
????????????throw;?// also reverts the transfer to Sharer??也恢復(fù)Sharer的轉(zhuǎn)移
????????return?this.balance;
??? }
}
目前,Solidity異常自動發(fā)生,有三種情況,?:
如果你訪問數(shù)組超出其長度 (即x[i]?where?i >= x.length)
如果一個(gè)通過消息調(diào)用的函數(shù)沒有正確的執(zhí)行結(jié)束(即gas用完,或本身拋出異常)。
在內(nèi)部,當(dāng)拋出異常時(shí) ,Solidity就執(zhí)行“非法jump”, 從而導(dǎo)致EMV(Ether虛擬機(jī))恢復(fù)所有更改狀態(tài)。這個(gè)原因是沒有安全的方法可以繼續(xù)執(zhí)行,?? 預(yù)期的結(jié)果沒有發(fā)生。由于我們想保留事務(wù)的原子性,(所以)最安全的做法是恢復(fù)所有更改,并使整個(gè)事務(wù)(或者至少調(diào)用)沒有受影響。
? Copyright 2015, Ethereum. Revision 37381072.
Built with?Sphinx?using a?theme?provided by?Read the Docs.
?Read the Docsv: latest?