<a href="http://blog.csdn.net/diandianxiyu_geek/article/details/77877841" target="_blank">【Solidity】1.一個Solidity源檔案的布局</a>
<a href="http://blog.csdn.net/diandianxiyu_geek/article/details/77892188" target="_blank">【Solidity】2.合約的結構體</a>
<a href="http://blog.csdn.net/diandianxiyu_geek/article/details/77930835" target="_blank">【Solidity】3.類型</a>
<a href="http://blog.csdn.net/diandianxiyu_geek/article/details/77942459" target="_blank">【Solidity】4.機關和全局可變量</a>
<a href="http://blog.csdn.net/diandianxiyu_geek/article/details/77964409" target="_blank">【Solidity】5.表達式和控制結構</a>
<a href="http://blog.csdn.net/diandianxiyu_geek/article/details/77981249" target="_blank">【Solidity】6. 合約</a>
<a href="http://blog.csdn.net/diandianxiyu_geek/article/details/77989384" target="_blank">【Solidity】7. 部件</a>
<a href="http://blog.csdn.net/diandianxiyu_geek/article/details/78016231" target="_blank">【Solidity】8. 雜項</a>
Solidity是一種靜态類型的語言,這意味着每個變量(州和地方)的類型需要被指定的(或至少已知的 - 見下文型扣)在編譯時。 Solidity提供了幾種可以組合形成複雜類型的基本類型。
另外,類型可以在含有運算符的表達式與彼此互動。 對于操作的快速參考,請參閱運算符的優先順序。
以下類型也稱為值類型,因為這些類型的變量将始終按值傳遞,即當它們用作函數參數或配置設定時,它們始終被複制。
<code>bool</code>:可能的值是常量<code>true</code>和<code>false</code>。
操作:
<code>!</code> (邏輯否定)
<code>&&</code>(邏輯連接配接,“和”)
<code>||</code> (邏輯分離,“或”)
<code>==</code>(相等)
<code>!=</code>(不等式)
運算符<code>||</code> 和<code>&&</code>應用常見的短路規則。 這意味着在表達式<code>f(x) || g(y)</code>,如果<code>f(x)</code>評估為真,即使可能有副作用,也不會評估<code>g(y)</code>。
<code>int/uint</code>:各種大小的有符号和無符号整數。 關鍵字<code>uint8</code>到<code>uint256</code>的步幅是8(無符号的8到256位)和<code>int8</code>到<code>int256</code>。 <code>uint</code>和<code>int</code>分别是<code>uint256</code>和<code>int256</code>的别名。
比較:<code><=</code>,<code><</code>,<code>==</code>,<code>!=</code>,<code>>=</code>,<code>></code>(評估為bool)
位操作符:<code>&</code>,<code>|</code>,<code>^</code>(按位異或),<code>〜</code>(按位取反)
算術運算符:<code>+</code>, <code>-</code> ,<code>一進制 -</code> ,<code>一進制 +</code>,<code>*</code>,<code>/</code>,<code>%</code>(其餘),<code>**</code>(幂),<code><<</code>(左移),<code>>></code>(右移)
除法總是截斷(它隻是編譯為EVM的DIV操作碼),但如果兩個運算符都是文字(或文字表達式),則它不會截斷。
除零和模量具有零引發運作時異常。
移位操作的結果是左操作數的類型。 表達式<code>x << y</code>等價于<code>x * 2 ** y</code>,<code>x >> y</code>等價于<code>x / 2 ** y</code>。 這意味着移位負數符号延伸。 移位操作使用負數會引發運作時異常。
警告:
由符号整數類型的負值的右移位所産生的結果是從那些其他的程式設計語言産生的不同。 在“Solidity”中,将右圖轉換為除法,是以偏移的負值将向零舍入(截斷)。 在其他程式設計語言中,負值的轉移權就像劃分為舍入(朝向負無窮大)。
位址:儲存一個20位元組值(Ethereum位址的大小)。 位址類型也有成員,并作為所有合約的基礎。
操作
<code><=</code>, <code><</code>, <code>==</code>, <code>!=</code>, <code>>=</code> 和 <code>></code>
<code>balance</code> 和 <code>transfer</code>
可以使用财産餘額查詢位址的餘額,并使用傳輸函數将以太網(以wei為機關)發送到位址:
如果x是合同位址,則其代碼(更具體地說:其回退函數(如果存在))将與傳送調用一起執行(這是EVM的限制,不能防止)。 如果執行任務無法運作,或以任何方式失敗,則以太網傳輸将被恢複,并且目前的合同将以異常方式停止。
<code>send</code>
send是低級對等的轉賬。 如果執行失敗,目前合同将不會以異常方式停止,但發送将傳回false。
<code>call</code>, <code>callcode</code> and <code>delegatecall</code>
此外,為了與不遵守ABI的合同進行接口,提供了任意數量的任意類型參數的函數調用。 這些參數被填充到32位元組并連接配接。 一個例外是第一個參數被編碼到正好四個位元組的情況。 在這種情況下,這裡沒有填充以允許使用功能簽名。
<code>call</code>傳回一個布爾值,訓示調用的函數是否終止(<code>true</code>)或引起EVM異常(<code>false</code>)。 不可能通路傳回的實際資料(為此,我們需要提前知道編碼和大小)。
以類似的方式,可以使用函數<code>delegatecall</code>:差別在于僅使用給定位址的代碼,所有其他方面(存儲,餘額,…)均取自目前的合同。 委托人的目的是使用存儲在另一個合同中的庫代碼。 使用者必須確定兩個合同中的存儲布局适合委托使用。 在homestead之前,隻有一個名為<code>callcode</code>的有限變體才可用,不能通路原始的<code>msg.sender</code>和<code>msg.value</code>值。
所有三個功能<code>call</code>, <code>callcode</code>和<code>delegatecall</code>都是非常低級的功能,隻能作為最後的手段,因為它們打破了Solidity的類型安全性。
<code>.gas()</code>選項可用于所有三種方法,而<code>delegatecall</code>不支援<code>.value()</code>選項。
所有的合約都繼承位址的成員,是以它是可以查詢使用<code>this.balance</code>目前合約的餘額。
不鼓勵使用<code>callcode</code>,将來會被删除。
所有這些功能都是低級别的功能,應小心使用。 具體而言,任何未知的合約可能是惡意的,如果你調用它,你交出控制權,以該合同可能又回調到你的合約,是以要改變你的狀态變量,當調用傳回準備。
<code>bytes1</code>,<code>bytes2</code>,<code>bytes3</code>,…,<code>bytes32</code>。 <code>byte</code>是<code>bytes1</code>的别名。
位運算符:<code>&</code>,<code>|</code>,<code>^</code>(按位異或),<code>〜</code>(逐位否定),<code><<</code>(左移),<code>>></code>(右移)
索引通路:如果x的類型為<code>bytesI</code>,則<code>0 <= k <I</code>的<code>x [k]</code>傳回第<code>k</code>個位元組(隻讀)。
移位操作符以任何整數類型作為右操作數(但将傳回左操作數的類型),表示要移位的位數。 移動負數将導緻運作時異常。
成員:
<code>.length</code>産生位元組數組的固定長度(隻讀)。
bytes:
動态大小的位元組數組,請參見數組。 不是價值型!
string:
動态尺寸的UTF-8編碼字元串,請參見數組。 不是價值型!
作為一個經驗法則,用于任意長度的原始位元組資料和字元串的任意長度的字元串(UTF-8)資料位元組。 如果可以将長度限制在一定數量的位元組,那麼請務必使用bytes1至bytes32之一,因為它們便宜得多。
固體點數目前尚未完全支援。 它們可以被聲明,但不能被配置設定到或來自。
通過位址校驗和測試的十六進制文字,例如0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF是位址類型。 長度在39到41位之間的十六進制文字不通過校驗和測試會産生警告,并被視為正常有理數字文字。
整數文字由0-9範圍内的數字序列組成。 它們被解釋為小數。 例如,69意味着六十九。 八進制文字不存在于粘性和前導零無效。
小數部分文字由一個<code>.</code>形成。 在一側至少有一個數字。 例子包括<code>1.</code>,<code>.1</code>和<code>1.3</code>。
也支援科學符号,其中基數可以分數,而指數不能。 執行個體包括<code>2e10</code>,<code>-2e10</code>,<code>2e-10</code>,<code>2.5e1</code>。
數字字面表達式保持任意精度,直到它們轉換為非文字類型(即通過将它們與非文字表達式一起使用)。 這意味着計算不會溢出,而在數字文字表達式中,分割不會截斷。
例如,<code>(2**800 + 1) - 2**800</code>導緻常數<code>1</code>(類型<code>uint8</code>),盡管中間結果甚至不适合機器字大小。 此外,<code>.5 * 8</code>導緻整數<code>4</code>(盡管在其間使用非整數)。
隻要操作數為整數,任何可應用于整數的運算符也可應用于數字文字表達式。 如果兩者中的任何一個是分數的,則不允許位操作,如果指數是分數(因為可能導緻非有理數),則不允許求幂)。
Solidity有一些文本類型為每個有理數。 整數文字和理性數字字面值屬于數字文字類型。 此外,所有數字字面值表達式(即僅包含數字文字和運算符的表達式)都屬于數字字面值類型。 是以數字文字表達式<code>1 + 2</code>和<code>2 + 1</code>都屬于理性數字3的相同數字字面值類型。
用于在早期版本中截斷的整數文字的分割,但現在将轉換為有理數,即<code>5/2</code>不等于<code>2</code>,但為<code>2.5</code>
因為它們與非文字表達式中使用的數字面表達式盡快轉換成非文字類型。 盡管我們知道,在下面的例子中配置設定給b中的表達式的值的計算結果為一個整數,但局部表達<code>2.5 + a</code>不類型檢查是以代碼不編譯.
字元串文字用雙引号或單引号(<code>"foo"</code>或<code>'bar'</code>)編寫。 它們并不意味着像C中那樣為零; <code>"foo"</code>代表三個不是四個位元組。 與整數文字一樣,它們的類型可以有所不同,但它們可以隐式轉換為<code>bytes1</code>,…,<code>bytes32</code>(如果它們适合)到位元組和字元串。
字元串文字支援轉義字元,例如<code>\n</code>,<code>\xNN</code>和<code>\uNNNN</code>。 <code>\xNN</code>取十六進制值并插入相應的位元組,而<code>\ uNNNN</code>則使用Unicode代碼點并插入UTF-8序列。
Hexademical Literals以關鍵字hex為字首,以雙引号或單引号(<code>hex"001122FF"</code>)括起來。 它們的内容必須是十六進制字元串,它們的值将是這些值的二進制表示。
Hexademical Literals像字元串文字,具有相同的可轉換性限制。
枚舉是在Solidity中建立使用者定義類型的一種方式。 它們可以顯式轉換為所有整數類型,也可以轉換為隐式轉換。 顯式轉換在運作時檢查值範圍,失敗會導緻異常。 枚舉需要至少一個成員。
函數類型是函數的類型。 函數類型的變量可以從函數配置設定,函數類型的函數參數可以用于将函數傳遞給函數并從函數調用傳回函數。 功能類型有兩種功能 - 内部和外部功能:
内部函數隻能在目前合約内部更詳細地調用(更具體地說是在目前代碼單元内部,也包括内部函數庫和繼承函數),因為它們不能在目前契約的上下文之外執行。 調用内部函數是通過跳轉到其入口标簽來實作的,就像在内部調用目前合同的函數一樣。
外部功能由一個位址和一個函數簽名,他們可以通過傳遞和外部函數調用傳回。
功能類型記為如下:
與參數類型相反,傳回類型不能為空 - 如果函數類型不傳回任何東西,則<code>returns (<return types>)</code>部分必須被省略。
預設情況下,函數類型為<code>internal</code>,内部關鍵字可以省略。 與此相反,合同函數本身預設為公用,隻能作為類型的名稱中使用時,預設為内部。
在目前合約中有兩種通路函數的方法:直接以其名稱,<code>f</code>或使用<code>this.f</code>. 前者将導緻内部功能,後者在外部功能中。
如果函數類型變量未初始化,則調用它将導緻異常。 如果在使用<code>delete</code>之後調用函數也會發生這種情況。
如果在Solidity的上下文中使用外部函數類型,則将它們視為函數類型,它以單個位元組24類型将該位址後跟功能辨別符編碼在一起。
請注意,現行合約的公共函數既可以作為内部函數也可以用作外部函數。 要使用<code>f</code>作為内部函數,隻需使用<code>f</code>,如果要使用其外部形式,請使用<code>this.f</code>.
顯示如何使用内部函數類型的示例:
另一個使用外部函數類型的例子:
Lambda或内聯函數已計劃但尚未支援。
複雜類型,即不總是适合256位的類型,必須比我們已經看到的值類型更仔細地處理。 由于複制它們可能相當昂貴,我們必須考慮我們是否希望将它們存儲在記憶體中(不是持久存儲的)或存儲(儲存狀态變量的位置)。
每個複雜類型,即數組和結構體,都有一個額外的注釋,即“資料位置”,關于它是存儲在記憶體中還是存儲器中。 根據上下文,總是預設,但可以通過将存儲或記憶體附加到類型來覆寫。 函數參數(包括傳回參數)預設值是記憶體,局部變量預設是存儲和位置被迫儲存狀态變量(顯然)。
還有第三個資料位置calldata,它是一個不可修改的非持久性區域,其中存儲了函數參數。 外部函數的函數參數(不傳回參數)被強制調用資料,并且主要表現為記憶體。
因為他們改變了配置設定的行為資料的位置是很重要的:存儲和記憶體,并以一個狀态變量(甚至是從其他狀态變量)之間的配置設定總是建立一個獨立的副本。 本地存儲變量的配置設定隻能配置設定一個引用,而且該引用總是指向狀态變量,即使後者在此同時被更改。 另一方面,從存儲的存儲引用類型到另一存儲器引用類型的配置設定不會建立副本。
總結:
強制資料位置:
- 外部函數的參數(不傳回):calldata
- 狀态變量:存儲
預設資料位置:
- 函數的參數(也傳回):記憶體
- 所有其他局部變量:存儲
數組可以具有編譯時固定大小,也可以是動态的。 對于存儲陣列,元素類型可以是任意的(也就是其他數組,映射或結構)。 對于存儲器陣列,它不能是映射,如果它是公共可見函數的參數,則必須是ABI類型。
固定大小k和元素類型T的數組被寫為<code>T [k]</code>,動态大小的數組為<code>T []</code>。 例如,uint的5個動态數組的數組是<code>uint [] [5]</code>(注意,與其他語言相比,符号是相反的)。 要通路第三個動态數組中的第二個uint,您使用<code>x [2] [1]</code>(索引為零,通路以相反的方式工作,即x [2]将類型中的一個級别 正确的)。
類型位元組和字元串的變量是特殊的數組。 一個位元組類似于byte [],但是它被緊密地打包在calldata中。 字元串等于位元組,但不允許長度或索引通路(現在)。
是以,位元組應該始終優先于byte [],因為它成本更低。
如果要通路字元串<code>s</code>的位元組表示,請使用<code>bytes(s).length</code> / <code>bytes(s)[7] = 'x'</code>;. 請記住,您正在通路UTF-8表示的低級位元組,而不是單個字元!
在記憶體中建立可變長度的數組可以使用<code>new</code>關鍵字來完成。 與存儲陣列相反,不能通過配置設定給<code>.length</code>成員來調整存儲器陣列的大小。
數組字面值是寫入表達式的數組,不會立即配置設定給變量。
數組文字的類型是固定大小的記憶體數組,其基類型是給定元素的常見類型。 [1,2,3]的類型是uint8 [3]記憶體,因為這些常量的類型是uint8。 是以,有必要将上述示例中的第一個元素轉換為uint。 請注意,目前,固定大小的存儲陣列不能配置設定給動态大小的存儲器陣列,即不可能實作以下功能:
計劃在将來删除這個限制,但由于在ABI中如何傳遞數組,是以目前還會産生一些并發症。
length:
數組有一個長度成員來儲存它們的元素數量。 可以通過更改.length成員來調整動态數組的大小(不在記憶體中)。 嘗試通路目前長度之外的元素時,不會自動發生。 一旦建立了存儲器陣列的大小是固定的(但是動态的,即它可以依賴于運作時參數)。
push:
動态存儲陣列和位元組(不是字元串)具有稱為push的成員函數,可用于在數組的末尾追加元素。 該函數傳回新的長度。
在外部函數中不可能使用陣列數組。
由于EVM的限制,不可能從外部函數調用傳回動态内容。 <code>contract C { function f() returns (uint[]) { ... }</code>将傳回一個如果從web3.js調用的東西,但是如果從Solidity調用則傳回。
現在唯一的解決方法是使用大型靜态大小的數組。
Solidity提供了一種以結構體形式定義新類型的方法,如下例所示:
合約沒有提供衆籌合約的全部功能,但它包含了解結構所必需的基本概念。 結構類型可以在映射和數組中使用,它們本身可以包含映射和數組。
結構體不可能包含自己類型的成員,盡管struct本身可以是映射成員的值類型。 這個限制是必要的,因為結構的大小必須是有限的。
請注意,在所有函數中,将struct類型配置設定給局部變量(預設存儲資料位置)。 這不會複制結構體,而隻存儲一個引用,以便對局部變量的成員的指派實際寫入狀态。
當然,您也可以直接通路結構的成員,而不必将其配置設定給本地變量,如廣告系列[campaignID] .amount = 0。
映射類型被聲明為<code>mapping(_KeyType => _ValueType)</code>。 這裡<code>_KeyType</code>可以是幾乎任何類型,除了映射,動态大小的數組,契約,枚舉和結構體。 <code>_ValueType</code>實際上可以是任何類型,包括映射。
是以,映射沒有長度或概念的鍵或值被設定。
映射隻允許用于狀态變量(或内部函數中的存儲引用類型)。
有可能<code>public</code>标記映射,并具有Solidity建立一個getter。 _KeyType将成為getter的必需參數,它将傳回_ValueType。
_ValueType也可以是映射。 getter将遞歸地為每個_KeyType設定一個參數。
如果<code>a</code>是一個LValue(即,可以配置設定給一個變量或東西),下面的運算符可作為簡寫:
<code>a += e</code>等價于<code>a = a + e</code>。 相應地定義運算符 <code>-=</code>,<code>*=</code>,<code>/=</code>,<code>%=</code>,<code>a |=</code>,<code>&=</code>和<code>^=</code>。 <code>a++</code>和<code>a--</code>等價于<code>a += 1</code> / <code>a -= 1</code> ,但表達式本身仍然具有以前的值<code>a</code>。 相反,<code>--a</code>和<code>++ a</code>對于a而言具有相同的效果,但在更改後傳回值。
<code>delete a</code>将類型的初始值配置設定給a。即 對于整數,它等效于a = 0,但它也可以用于數組,其中配置設定長度為零的動态數組或與所有元素重置的長度相同的靜态數組。 對于結構體,它會為所有成員重新配置設定一個結構體。
删除對整個映射沒有影響(因為映射的關鍵字可能是任意的,并且通常是未知的)。 是以,如果你删除一個結構體,它将會重置所有不是映射的成員,也可以遞歸到成員中,除非是映射。 但是,可以删除單個鍵及其映射到的内容。
如果運算符應用于不同類型,編譯器将嘗試将其中一個操作數隐式轉換為其他類型(對于指派也是如此)。 一般來說,值類型之間的隐式轉換是有可能的,如果它在語義上有意義且沒有資訊丢失:uint8可以轉換為uint16和int128到int256,但int8不能轉換為uint256(因為uint256不能保持為-1)。 此外,無符号整數可以轉換為相同或更大尺寸的位元組,但反之亦然。 任何可以轉換為uint160的類型也可以轉換為位址。
如果編譯器不允許隐式轉換,但是你知道你正在做什麼,那麼有時可以使用顯式類型轉換。 請注意,這可能會給您一些意想不到的行為,是以一定要測試,以確定結果是你想要的! 以下示例将負的int8轉換為uint:
在這段代碼片段末尾,x将具有0xfffff..fd(64個十六進制字元)的值,在256位的二進制補碼表示中為-3。
如果一個類型被明确轉換為較小的類型,則高階位被切斷:
為友善起見,并不總是需要明确指定變量的類型,編譯器會根據配置設定給該變量的第一個表達式的類型自動推斷:
這裡,y的類型将是uint24。 對于函數參數或傳回參數,不能使用var。
該類型僅從第一個指派中推導出來,是以以下代碼段中的循環是無限的,因為我将具有類型uint8,并且此類型的任何值都小于2000.對于<code>for (var i = 0; i < 2000; i++) { ... }</code>