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