【Solidity】3.类型 - 深入理解Solidity
索引
- 【Solidity】1.一個Solidity源文件的布局
- 【Solidity】2.合約的結(jié)構(gòu)體
- 【Solidity】3.類型
- 【Solidity】4.單位和全局可變量
- 【Solidity】5.表達(dá)式和控制結(jié)構(gòu)
- 【Solidity】6. 合約
- 【Solidity】7. 部件
- 【Solidity】8. 雜項
類型
Solidity是一種靜態(tài)類型的語言,這意味著每個變量(州和地方)的類型需要被指定的(或至少已知的 - 見下文型扣)在編譯時。 Solidity提供了幾種可以組合形成復(fù)雜類型的基本類型。
另外,類型可以在含有運(yùn)算符的表達(dá)式與彼此交互。 對于操作的快速參考,請參閱運(yùn)算符的優(yōu)先順序。
值類型
以下類型也稱為值類型,因為這些類型的變量將始終按值傳遞,即當(dāng)它們用作函數(shù)參數(shù)或分配時,它們始終被復(fù)制。
布爾
bool:可能的值是常量true和false。
操作:
- ! (邏輯否定)
- &&(邏輯連接,“和”)
- || (邏輯分離,“或”)
- ==(相等)
- !=(不等式)
運(yùn)算符|| 和&&應(yīng)用常見的短路規(guī)則。 這意味著在表達(dá)式f(x) || g(y),如果f(x)評估為真,即使可能有副作用,也不會評估g(y)。
整型
int/uint:各種大小的有符號和無符號整數(shù)。 關(guān)鍵字uint8到uint256的步幅是8(無符號的8到256位)和int8到int256。 uint和int分別是uint256和int256的別名。
操作:
- 比較:<=,<,==,!=,>=,>(評估為bool)
- 位操作符:&,|,^(按位異或),?(按位取反)
- 算術(shù)運(yùn)算符:+, - ,一元 - ,一元 +,*,/,%(其余),**(冪),<<(左移),>>(右移)
除法總是截斷(它只是編譯為EVM的DIV操作碼),但如果兩個運(yùn)算符都是文字(或文字表達(dá)式),則它不會截斷。
除零和模量具有零引發(fā)運(yùn)行時異常。
移位操作的結(jié)果是左操作數(shù)的類型。 表達(dá)式x << y等價于x * 2 ** y,x >> y等價于x / 2 ** y。 這意味著移位負(fù)數(shù)符號延伸。 移位操作使用負(fù)數(shù)會引發(fā)運(yùn)行時異常。
警告:
由符號整數(shù)類型的負(fù)值的右移位所產(chǎn)生的結(jié)果是從那些其他的編程語言產(chǎn)生的不同。 在“Solidity”中,將右圖轉(zhuǎn)換為除法,所以偏移的負(fù)值將向零舍入(截斷)。 在其他編程語言中,負(fù)值的轉(zhuǎn)移權(quán)就像劃分為舍入(朝向負(fù)無窮大)。
地址 Address
地址:保存一個20字節(jié)值(Ethereum地址的大小)。 地址類型也有成員,并作為所有合約的基礎(chǔ)。
操作
- <=, <, ==, !=, >= 和 >
地址成員 Members of Addresses
- balance 和 transfer
有關(guān)快速參考,請參閱地址相關(guān)。
可以使用財產(chǎn)余額查詢地址的余額,并使用傳輸函數(shù)將以太網(wǎng)(以wei為單位)發(fā)送到地址:
address x = 0x123; address myAddress = this; if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);如果x是合同地址,則其代碼(更具體地說:其回退函數(shù)(如果存在))將與傳送調(diào)用一起執(zhí)行(這是EVM的限制,不能防止)。 如果執(zhí)行任務(wù)無法運(yùn)行,或以任何方式失敗,則以太網(wǎng)傳輸將被恢復(fù),并且當(dāng)前的合同將以異常方式停止。
- send
send是低級對等的轉(zhuǎn)賬。 如果執(zhí)行失敗,當(dāng)前合同將不會以異常方式停止,但發(fā)送將返回false。
- call, callcode and delegatecall
此外,為了與不遵守ABI的合同進(jìn)行接口,提供了任意數(shù)量的任意類型參數(shù)的函數(shù)調(diào)用。 這些參數(shù)被填充到32字節(jié)并連接。 一個例外是第一個參數(shù)被編碼到正好四個字節(jié)的情況。 在這種情況下,這里沒有填充以允許使用功能簽名。
address nameReg = 0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2; nameReg.call("register", "MyName"); nameReg.call(bytes4(keccak256("fun(uint256)")), a);call返回一個布爾值,指示調(diào)用的函數(shù)是否終止(true)或引起EVM異常(false)。 不可能訪問返回的實際數(shù)據(jù)(為此,我們需要提前知道編碼和大小)。
以類似的方式,可以使用函數(shù)delegatecall:區(qū)別在于僅使用給定地址的代碼,所有其他方面(存儲,余額,…)均取自當(dāng)前的合同。 委托人的目的是使用存儲在另一個合同中的庫代碼。 用戶必須確保兩個合同中的存儲布局適合委托使用。 在homestead之前,只有一個名為callcode的有限變體才可用,不能訪問原始的msg.sender和msg.value值。
所有三個功能call, callcode和delegatecall都是非常低級的功能,只能作為最后的手段,因為它們打破了Solidity的類型安全性。
.gas()選項可用于所有三種方法,而delegatecall不支持.value()選項。
所有的合約都繼承地址的成員,所以它是可以查詢使用this.balance當(dāng)前合約的余額。
不鼓勵使用callcode,將來會被刪除。
所有這些功能都是低級別的功能,應(yīng)小心使用。 具體而言,任何未知的合約可能是惡意的,如果你調(diào)用它,你交出控制權(quán),以該合同可能又回調(diào)到你的合約,所以要改變你的狀態(tài)變量,當(dāng)調(diào)用返回準(zhǔn)備。
固定大小的字節(jié)數(shù)組
bytes1,bytes2,bytes3,…,bytes32。 byte是bytes1的別名。
操作:
- 比較:<=,<,==,!=,>=,>(評估為bool)
- 位運(yùn)算符:&,|,^(按位異或),?(逐位否定),<<(左移),>>(右移)
- 索引訪問:如果x的類型為bytesI,則0 <= k <I的x [k]返回第k個字節(jié)(只讀)。
移位操作符以任何整數(shù)類型作為右操作數(shù)(但將返回左操作數(shù)的類型),表示要移位的位數(shù)。 移動負(fù)數(shù)將導(dǎo)致運(yùn)行時異常。
成員:
.length產(chǎn)生字節(jié)數(shù)組的固定長度(只讀)。
動態(tài)大小的字節(jié)數(shù)組
bytes:
動態(tài)大小的字節(jié)數(shù)組,請參見數(shù)組。 不是價值型!
string:
動態(tài)尺寸的UTF-8編碼字符串,請參見數(shù)組。 不是價值型!
作為一個經(jīng)驗法則,用于任意長度的原始字節(jié)數(shù)據(jù)和字符串的任意長度的字符串(UTF-8)數(shù)據(jù)字節(jié)。 如果可以將長度限制在一定數(shù)量的字節(jié),那么請務(wù)必使用bytes1至bytes32之一,因為它們便宜得多。
固定點數(shù) Fixed Point Numbers
固體點數(shù)目前尚未完全支持。 它們可以被聲明,但不能被分配到或來自。
地址文字 Address Literals
通過地址校驗和測試的十六進(jìn)制文字,例如0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF是地址類型。 長度在39到41位之間的十六進(jìn)制文字不通過校驗和測試會產(chǎn)生警告,并被視為常規(guī)有理數(shù)字文字。
理性和整數(shù)文字 Rational and Integer Literals
整數(shù)文字由0-9范圍內(nèi)的數(shù)字序列組成。 它們被解釋為小數(shù)。 例如,69意味著六十九。 八進(jìn)制文字不存在于粘性和前導(dǎo)零無效。
小數(shù)部分文字由一個.形成。 在一側(cè)至少有一個數(shù)字。 例子包括1.,.1和1.3。
也支持科學(xué)符號,其中基數(shù)可以分?jǐn)?shù),而指數(shù)不能。 實例包括2e10,-2e10,2e-10,2.5e1。
數(shù)字字面表達(dá)式保持任意精度,直到它們轉(zhuǎn)換為非文字類型(即通過將它們與非文字表達(dá)式一起使用)。 這意味著計算不會溢出,而在數(shù)字文字表達(dá)式中,分割不會截斷。
例如,(2**800 + 1) - 2**800導(dǎo)致常數(shù)1(類型uint8),盡管中間結(jié)果甚至不適合機(jī)器字大小。 此外,.5 * 8導(dǎo)致整數(shù)4(盡管在其間使用非整數(shù))。
只要操作數(shù)為整數(shù),任何可應(yīng)用于整數(shù)的運(yùn)算符也可應(yīng)用于數(shù)字文字表達(dá)式。 如果兩者中的任何一個是分?jǐn)?shù)的,則不允許位操作,如果指數(shù)是分?jǐn)?shù)(因為可能導(dǎo)致非有理數(shù)),則不允許求冪)。
Solidity有一些文本類型為每個有理數(shù)。 整數(shù)文字和理性數(shù)字字面值屬于數(shù)字文字類型。 此外,所有數(shù)字字面值表達(dá)式(即僅包含數(shù)字文字和運(yùn)算符的表達(dá)式)都屬于數(shù)字字面值類型。 所以數(shù)字文字表達(dá)式1 + 2和2 + 1都屬于理性數(shù)字3的相同數(shù)字字面值類型。
用于在早期版本中截斷的整數(shù)文字的分割,但現(xiàn)在將轉(zhuǎn)換為有理數(shù),即5/2不等于2,但為2.5
因為它們與非文字表達(dá)式中使用的數(shù)字面表達(dá)式盡快轉(zhuǎn)換成非文字類型。 盡管我們知道,在下面的例子中分配給b中的表達(dá)式的值的計算結(jié)果為一個整數(shù),但局部表達(dá)2.5 + a不類型檢查所以代碼不編譯.
uint128 a = 1; uint128 b = 2.5 + a + 0.5;字符串文字 String Literals
字符串文字用雙引號或單引號("foo"或'bar')編寫。 它們并不意味著像C中那樣為零; "foo"代表三個不是四個字節(jié)。 與整數(shù)文字一樣,它們的類型可以有所不同,但它們可以隱式轉(zhuǎn)換為bytes1,…,bytes32(如果它們適合)到字節(jié)和字符串。
字符串文字支持轉(zhuǎn)義字符,例如\n,\xNN和\uNNNN。 \xNN取十六進(jìn)制值并插入相應(yīng)的字節(jié),而\ uNNNN則使用Unicode代碼點并插入UTF-8序列。
十六進(jìn)制文字 Hexadecimal Literals
Hexademical Literals以關(guān)鍵字hex為前綴,以雙引號或單引號(hex"001122FF")括起來。 它們的內(nèi)容必須是十六進(jìn)制字符串,它們的值將是這些值的二進(jìn)制表示。
Hexademical Literals像字符串文字,具有相同的可轉(zhuǎn)換性限制。
枚舉
枚舉是在Solidity中創(chuàng)建用戶定義類型的一種方式。 它們可以顯式轉(zhuǎn)換為所有整數(shù)類型,也可以轉(zhuǎn)換為隱式轉(zhuǎn)換。 顯式轉(zhuǎn)換在運(yùn)行時檢查值范圍,失敗會導(dǎo)致異常。 枚舉需要至少一個成員。
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的一部分,所以對于Solidity之外的所有事務(wù),“getChoice”的簽名將自動更改為“getChoice()return(uint8)”)。 所使用的整數(shù)類型足夠大以容納所有枚舉值,即如果您有更多值,則將使用`uint16`等等。function getChoice() returns (ActionChoices) {return choice;}function getDefaultChoice() returns (uint) {return uint(defaultChoice);} }函數(shù)類型 Function Types
函數(shù)類型是函數(shù)的類型。 函數(shù)類型的變量可以從函數(shù)分配,函數(shù)類型的函數(shù)參數(shù)可以用于將函數(shù)傳遞給函數(shù)并從函數(shù)調(diào)用返回函數(shù)。 功能類型有兩種功能 - 內(nèi)部和外部功能:
內(nèi)部函數(shù)只能在當(dāng)前合約內(nèi)部更詳細(xì)地調(diào)用(更具體地說是在當(dāng)前代碼單元內(nèi)部,也包括內(nèi)部函數(shù)庫和繼承函數(shù)),因為它們不能在當(dāng)前契約的上下文之外執(zhí)行。 調(diào)用內(nèi)部函數(shù)是通過跳轉(zhuǎn)到其入口標(biāo)簽來實現(xiàn)的,就像在內(nèi)部調(diào)用當(dāng)前合同的函數(shù)一樣。
外部功能由一個地址和一個函數(shù)簽名,他們可以通過傳遞和外部函數(shù)調(diào)用返回。
功能類型記為如下:
function (<parameter types>) {internal|external} [pure|constant|view|payable] [returns (<return types>)] function (<參數(shù)類型>) {internal|external} [pure|constant|view|payable] [returns (<返回類型>)]與參數(shù)類型相反,返回類型不能為空 - 如果函數(shù)類型不返回任何東西,則returns (<return types>)部分必須被省略。
缺省情況下,函數(shù)類型為internal,內(nèi)部關(guān)鍵字可以省略。 與此相反,合同函數(shù)本身默認(rèn)為公用,只能作為類型的名稱中使用時,默認(rèn)為內(nèi)部。
在當(dāng)前合約中有兩種訪問函數(shù)的方法:直接以其名稱,f或使用this.f. 前者將導(dǎo)致內(nèi)部功能,后者在外部功能中。
如果函數(shù)類型變量未初始化,則調(diào)用它將導(dǎo)致異常。 如果在使用delete之后調(diào)用函數(shù)也會發(fā)生這種情況。
如果在Solidity的上下文中使用外部函數(shù)類型,則將它們視為函數(shù)類型,它以單個字節(jié)24類型將該地址后跟功能標(biāo)識符編碼在一起。
請注意,現(xiàn)行合約的公共函數(shù)既可以作為內(nèi)部函數(shù)也可以用作外部函數(shù)。 要使用f作為內(nèi)部函數(shù),只需使用f,如果要使用其外部形式,請使用this.f.
顯示如何使用內(nèi)部函數(shù)類型的示例:
pragma solidity ^0.4.5;library ArrayUtils {// 內(nèi)部函數(shù)可以在內(nèi)部函數(shù)庫中使用,因為它們將成為相同代碼上下文的一部分//參數(shù),整數(shù)數(shù)組、函數(shù)、內(nèi)部的整數(shù);內(nèi)部;返回整數(shù)數(shù)組function map(uint[] memory self, function (uint) returns (uint) f)internalreturns (uint[] memory r){r = new uint[](self.length); //定義一個固定長度的整數(shù)數(shù)組for (uint i = 0; i < self.length; i++) {r[i] = f(self[i]); //每個元素都執(zhí)行作為參數(shù)的函數(shù),返回值寫入上面定義的數(shù)組}}//參數(shù),整形數(shù)組,參數(shù)為兩個整形返回值為一個整形的函數(shù);內(nèi)部的;返回整形function reduce(uint[] memory self,function (uint, uint) returns (uint) f)internalreturns (uint r){r = self[0]; //初始值為整形數(shù)組的第一個元素for (uint i = 1; i < self.length; i++) {r = f(r, self[i]); //遍歷執(zhí)行作為參數(shù)的函數(shù),返回值賦值給當(dāng)前值}}// 參數(shù)為長度,內(nèi)部,返回數(shù)組,返回值用于上下文function range(uint length) internal returns (uint[] memory r) {r = new uint[](length); //r為定義一個固定長度的數(shù)組for (uint i = 0; i < r.length; i++) {r[i] = i; // 以此寫入0 1 2 ..}} }//金字塔 contract Pyramid {using ArrayUtils for *;//參數(shù),整形;返回整形function pyramid(uint l) returns (uint) {//得到一個數(shù)組形如 arr = [0,1,2,3,4] ,各項的平方后返回新的數(shù)組,數(shù)組的各個元素相加求和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 {//定義結(jié)構(gòu)體struct Request {bytes data; //日期function(bytes memory) external callback; }Request[] requests; //定義全局變量,數(shù)組event NewRequest(uint); //定義事件,參數(shù)為整形//函數(shù),參數(shù),日期、參數(shù)為字節(jié)、外部、回調(diào)的函數(shù)function query(bytes data, function(bytes memory) external callback) {requests.push(Request(data, callback)); //壓入全局?jǐn)?shù)組NewRequest(requests.length - 1); //復(fù)制給事件的值為當(dāng)前全局變量數(shù)組的長度}//參數(shù),整形,字節(jié)function reply(uint requestID, bytes response) {// 這里檢查答復(fù)是來自可信來源requests[requestID].callback(response); //通過callback方法執(zhí)行} }//定義合約 contract OracleUser {Oracle constant oracle = Oracle(0x1234567); // 已知的合約//調(diào)用function buySomething() {//調(diào)用外部合約的方法,使用的第二個參數(shù)為當(dāng)前合約的方法oracle.query("USD", this.oracleResponse);}//參數(shù)為字節(jié)function oracleResponse(bytes response) {require(msg.sender == address(oracle)); //如果消息發(fā)送的人和。。。相等// Use the data} }Lambda或內(nèi)聯(lián)函數(shù)已計劃但尚未支持。
引用類型
復(fù)雜類型,即不總是適合256位的類型,必須比我們已經(jīng)看到的值類型更仔細(xì)地處理。 由于復(fù)制它們可能相當(dāng)昂貴,我們必須考慮我們是否希望將它們存儲在內(nèi)存中(不是持久存儲的)或存儲(保存狀態(tài)變量的位置)。
數(shù)據(jù)位置 Data location
每個復(fù)雜類型,即數(shù)組和結(jié)構(gòu)體,都有一個額外的注釋,即“數(shù)據(jù)位置”,關(guān)于它是存儲在內(nèi)存中還是存儲器中。 根據(jù)上下文,總是默認(rèn),但可以通過將存儲或內(nèi)存附加到類型來覆蓋。 函數(shù)參數(shù)(包括返回參數(shù))默認(rèn)值是內(nèi)存,局部變量默認(rèn)是存儲和位置被迫儲存狀態(tài)變量(顯然)。
還有第三個數(shù)據(jù)位置calldata,它是一個不可修改的非持久性區(qū)域,其中存儲了函數(shù)參數(shù)。 外部函數(shù)的函數(shù)參數(shù)(不返回參數(shù))被強(qiáng)制調(diào)用數(shù)據(jù),并且主要表現(xiàn)為內(nèi)存。
因為他們改變了分配的行為數(shù)據(jù)的位置是很重要的:存儲和內(nèi)存,并以一個狀態(tài)變量(甚至是從其他狀態(tài)變量)之間的分配總是創(chuàng)建一個獨(dú)立的副本。 本地存儲變量的分配只能分配一個引用,而且該引用總是指向狀態(tài)變量,即使后者在此同時被更改。 另一方面,從存儲的存儲引用類型到另一存儲器引用類型的分配不會創(chuàng)建副本。
pragma solidity ^0.4.0;contract C {uint[] x; // x的數(shù)據(jù)位置是存儲// memoryArray的數(shù)據(jù)位置是內(nèi)存function f(uint[] memoryArray) {x = memoryArray; // 工作,將整個數(shù)組復(fù)制到存儲var y = x; // 工作,分配一個指針,y的數(shù)據(jù)位置是存儲y[7]; // fine,返回第8個元素y.length = 2; // fine, 通過y修改xdelete x; // fine, 清除數(shù)組,修改y// 以下不工作; 它將需要在存儲中創(chuàng)建一個新的臨時未命名數(shù)組,但存儲是“靜態(tài)”分配的:// y = memoryArray;//這也不行,因為它會“重置”指針,但沒有明確的位置,它可以指向。 刪除y;g(x); // 調(diào)用 g,移交到x的引用h(x); // 調(diào)用 h ,在內(nèi)存中創(chuàng)建一個獨(dú)立的臨時副本}function g(uint[] storage storageArray) internal {}function h(uint[] memoryArray) {} }總結(jié):
強(qiáng)制數(shù)據(jù)位置:
- 外部函數(shù)的參數(shù)(不返回):calldata
- 狀態(tài)變量:存儲
默認(rèn)數(shù)據(jù)位置:
- 函數(shù)的參數(shù)(也返回):內(nèi)存
- 所有其他局部變量:存儲
數(shù)組
數(shù)組可以具有編譯時固定大小,也可以是動態(tài)的。 對于存儲陣列,元素類型可以是任意的(也就是其他數(shù)組,映射或結(jié)構(gòu))。 對于存儲器陣列,它不能是映射,如果它是公共可見函數(shù)的參數(shù),則必須是ABI類型。
固定大小k和元素類型T的數(shù)組被寫為T [k],動態(tài)大小的數(shù)組為T []。 例如,uint的5個動態(tài)數(shù)組的數(shù)組是uint [] [5](注意,與其他語言相比,符號是相反的)。 要訪問第三個動態(tài)數(shù)組中的第二個uint,您使用x [2] [1](索引為零,訪問以相反的方式工作,即x [2]將類型中的一個級別 正確的)。
類型字節(jié)和字符串的變量是特殊的數(shù)組。 一個字節(jié)類似于byte [],但是它被緊密地打包在calldata中。 字符串等于字節(jié),但不允許長度或索引訪問(現(xiàn)在)。
因此,字節(jié)應(yīng)該始終優(yōu)先于byte [],因為它成本更低。
如果要訪問字符串s的字節(jié)表示,請使用bytes(s).length / bytes(s)[7] = 'x';. 請記住,您正在訪問UTF-8表示的低級字節(jié),而不是單個字符!
可以將數(shù)組標(biāo)記為public,并且Solidity創(chuàng)建一個getter。 數(shù)字索引將成為getter必需的參數(shù)。
分配內(nèi)存數(shù)組 Allocating Memory Array
在內(nèi)存中創(chuàng)建可變長度的數(shù)組可以使用new關(guān)鍵字來完成。 與存儲陣列相反,不能通過分配給.length成員來調(diào)整存儲器陣列的大小。
pragma solidity ^0.4.0;contract C {function f(uint len) {uint[] memory a = new uint[](7); //第7個bytes memory b = new bytes(len);// Here we have a.length == 7 and b.length == lena[6] = 8;} }數(shù)組文字/內(nèi)聯(lián)數(shù)組 Array Literals / Inline Arrays
數(shù)組字面值是寫入表達(dá)式的數(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]的類型是uint8 [3]內(nèi)存,因為這些常量的類型是uint8。 因此,有必要將上述示例中的第一個元素轉(zhuǎn)換為uint。 請注意,目前,固定大小的存儲陣列不能分配給動態(tài)大小的存儲器陣列,即不可能實現(xiàn)以下功能:
// 這不會編譯。pragma solidity ^0.4.0;contract C {function f() {//下一行創(chuàng)建一個類型錯誤,因為uint [3]內(nèi)存//無法轉(zhuǎn)換為uint []內(nèi)存。uint[] x = [uint(1), 3, 4];} }計劃在將來刪除這個限制,但由于在ABI中如何傳遞數(shù)組,因此目前還會產(chǎn)生一些并發(fā)癥。
成員
length:
數(shù)組有一個長度成員來保存它們的元素數(shù)量。 可以通過更改.length成員來調(diào)整動態(tài)數(shù)組的大小(不在內(nèi)存中)。 嘗試訪問當(dāng)前長度之外的元素時,不會自動發(fā)生。 一旦創(chuàng)建了存儲器陣列的大小是固定的(但是動態(tài)的,即它可以依賴于運(yùn)行時參數(shù))。
push:
動態(tài)存儲陣列和字節(jié)(不是字符串)具有稱為push的成員函數(shù),可用于在數(shù)組的末尾追加元素。 該函數(shù)返回新的長度。
在外部函數(shù)中不可能使用陣列數(shù)組。
由于EVM的限制,不可能從外部函數(shù)調(diào)用返回動態(tài)內(nèi)容。 contract C { function f() returns (uint[]) { ... }將返回一個如果從web3.js調(diào)用的東西,但是如果從Solidity調(diào)用則返回。
現(xiàn)在唯一的解決方法是使用大型靜態(tài)大小的數(shù)組。
pragma solidity ^0.4.0;contract ArrayContract {uint[2**20] m_aLotOfIntegers;// Note that the following is not a pair of dynamic arrays but a// dynamic array of pairs (i.e. of fixed size arrays of length two).// 注意,以下不是一對動態(tài)數(shù)組,但對一個動態(tài)陣列(長度為兩個的固定大小陣列即)。bool[2][] m_pairsOfFlags;// newPairs存儲在內(nèi)存中 - 函數(shù)參數(shù)的默認(rèn)值function setAllFlagPairs(bool[2][] newPairs) {// 分配到一個存儲陣列替換整個陣列m_pairsOfFlags = newPairs;}function setFlagPair(uint index, bool flagA, bool flagB) {// 訪問不存在的索引將拋出異常m_pairsOfFlags[index][0] = flagA;m_pairsOfFlags[index][1] = flagB;}function changeFlagArraySize(uint newSize) {// 如果新的大小較小,則刪除的數(shù)組元素將被清除m_pairsOfFlags.length = newSize;}function clear() {// 這些完全清除了陣列delete m_pairsOfFlags;delete m_aLotOfIntegers;// 相同的效果在這里m_pairsOfFlags.length = 0;}bytes m_byteData;function byteArrays(bytes data) {// 字節(jié)數(shù)組(“bytes”)是不同的,因為它們在沒有填充的情況下被存儲,但可以被視為與“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`創(chuàng)建:uint[2][] memory arrayOfPairs = new uint[2][](size);// 創(chuàng)建一個動態(tài)字節(jié)數(shù)組:bytes memory b = new bytes(200);for (uint i = 0; i < b.length; i++)b[i] = byte(i);return b;} }結(jié)構(gòu)體 Structs
Solidity提供了一種以結(jié)構(gòu)體形式定義新類型的方法,如下例所示:
pragma solidity ^0.4.11;contract CrowdFunding {// 定義一個類型有兩個字段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)并保存在存儲中。 我們忽略了映射類型。campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);}function contribute(uint campaignID) payable {Campaign storage c = campaigns[campaignID];// 創(chuàng)建一個新的臨時內(nèi)存結(jié)構(gòu),用給定值初始化并將其復(fù)制到存儲。//你也可以通過 Funder(msg.sender, msg.value) 進(jìn)行初始化c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value});c.amount += msg.value;}function checkGoalReached(uint campaignID) returns (bool reached) {Campaign storage c = campaigns[campaignID];if (c.amount < c.fundingGoal)return false;uint amount = c.amount;c.amount = 0;c.beneficiary.transfer(amount);return true;} }合約沒有提供眾籌合約的全部功能,但它包含理解結(jié)構(gòu)所必需的基本概念。 結(jié)構(gòu)類型可以在映射和數(shù)組中使用,它們本身可以包含映射和數(shù)組。
結(jié)構(gòu)體不可能包含自己類型的成員,盡管struct本身可以是映射成員的值類型。 這個限制是必要的,因為結(jié)構(gòu)的大小必須是有限的。
請注意,在所有函數(shù)中,將struct類型分配給局部變量(默認(rèn)存儲數(shù)據(jù)位置)。 這不會復(fù)制結(jié)構(gòu)體,而只存儲一個引用,以便對局部變量的成員的賦值實際寫入狀態(tài)。
當(dāng)然,您也可以直接訪問結(jié)構(gòu)的成員,而不必將其分配給本地變量,如廣告系列[campaignID] .amount = 0。
映射 Mappings
映射類型被聲明為mapping(_KeyType => _ValueType)。 這里_KeyType可以是幾乎任何類型,除了映射,動態(tài)大小的數(shù)組,契約,枚舉和結(jié)構(gòu)體。 _ValueType實際上可以是任何類型,包括映射。
映射可以看作是虛擬初始化的哈希表,使得每個可能的鍵都存在,并被映射到一個值,其字節(jié)表示全為零:一個類型的默認(rèn)值。 相似之處在此結(jié)束,但是:密鑰數(shù)據(jù)實際上并不存儲在映射中,只有其keccak256哈希用于查找該值。
因此,映射沒有長度或概念的鍵或值被設(shè)置。
映射只允許用于狀態(tài)變量(或內(nèi)部函數(shù)中的存儲引用類型)。
有可能public標(biāo)記映射,并具有Solidity創(chuàng)建一個getter。 _KeyType將成為getter的必需參數(shù),它將返回_ValueType。
_ValueType也可以是映射。 getter將遞歸地為每個_KeyType設(shè)置一個參數(shù)。
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) {MappingExample m = new MappingExample();m.update(100);return m.balances(this);} }操作者涉及LValues Operators Involving LValues
如果a是一個LValue(即,可以分配給一個變量或東西),下面的運(yùn)算符可作為簡寫:
a += e等價于a = a + e。 相應(yīng)地定義運(yùn)算符 -=,*=,/=,%=,a |=,&=和^=。 a++和a--等價于a += 1 / a -= 1 ,但表達(dá)式本身仍然具有以前的值a。 相反,--a和++ a對于a而言具有相同的效果,但在更改后返回值。
delete
delete a將類型的初始值分配給a。即 對于整數(shù),它等效于a = 0,但它也可以用于數(shù)組,其中分配長度為零的動態(tài)數(shù)組或與所有元素重置的長度相同的靜態(tài)數(shù)組。 對于結(jié)構(gòu)體,它會為所有成員重新分配一個結(jié)構(gòu)體。
刪除對整個映射沒有影響(因為映射的關(guān)鍵字可能是任意的,并且通常是未知的)。 所以,如果你刪除一個結(jié)構(gòu)體,它將會重置所有不是映射的成員,也可以遞歸到成員中,除非是映射。 但是,可以刪除單個鍵及其映射到的內(nèi)容。
pragma solidity ^0.4.0;contract DeleteExample {uint data;uint[] dataArray;function f() {uint x = data;delete x; // 將x設(shè)置為0,不影響數(shù)據(jù)delete data; // 將數(shù)據(jù)設(shè)置為0,不影響仍然保存副本的xuint[] y = dataArray;delete dataArray; // 這將dataArray.length設(shè)置為零,但是由于uint []是一個復(fù)雜對象,所以受影響也是存儲對象的別名。另一方面:“delete y”無效,因為引用存儲的本地變量的賦值 對象只能由現(xiàn)有的存儲對象進(jìn)行。} }基本類型之間的轉(zhuǎn)換
隱性轉(zhuǎn)換
如果運(yùn)算符應(yīng)用于不同類型,編譯器將嘗試將其中一個操作數(shù)隱式轉(zhuǎn)換為其他類型(對于賦值也是如此)。 一般來說,值類型之間的隱式轉(zhuǎn)換是有可能的,如果它在語義上有意義且沒有信息丟失:uint8可以轉(zhuǎn)換為uint16和int128到int256,但int8不能轉(zhuǎn)換為uint256(因為uint256不能保持為-1)。 此外,無符號整數(shù)可以轉(zhuǎn)換為相同或更大尺寸的字節(jié),但反之亦然。 任何可以轉(zhuǎn)換為uint160的類型也可以轉(zhuǎn)換為地址。
顯式轉(zhuǎn)換
如果編譯器不允許隱式轉(zhuǎn)換,但是你知道你正在做什么,那么有時可以使用顯式類型轉(zhuǎn)換。 請注意,這可能會給您一些意想不到的行為,所以一定要測試,以確保結(jié)果是你想要的! 以下示例將負(fù)的int8轉(zhuǎn)換為uint:
int8 y = -3; uint x = uint(y);在這段代碼片段末尾,x將具有0xfffff..fd(64個十六進(jìn)制字符)的值,在256位的二進(jìn)制補(bǔ)碼表示中為-3。
如果一個類型被明確轉(zhuǎn)換為較小的類型,則高階位被切斷:
uint32 a = 0x12345678; uint16 b = uint16(a); // b will be 0x5678 now類型推導(dǎo)
為方便起見,并不總是需要明確指定變量的類型,編譯器會根據(jù)分配給該變量的第一個表達(dá)式的類型自動推斷:
uint24 x = 0x123; var y = x;這里,y的類型將是uint24。 對于函數(shù)參數(shù)或返回參數(shù),不能使用var。
該類型僅從第一個賦值中推導(dǎo)出來,所以以下代碼段中的循環(huán)是無限的,因為我將具有類型uint8,并且此類型的任何值都小于2000.對于for (var i = 0; i < 2000; i++) { ... }
總結(jié)
以上是生活随笔為你收集整理的【Solidity】3.类型 - 深入理解Solidity的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ArrayList实现
- 下一篇: ISE14.7兼容性问题集锦