類型
Solidity是一種靜态類型語言,意思是每個變量(聲明和本地)在編譯時刻都要定義 (或者至少要知曉,參看 後面的類型導出 )。
Solidity提供幾個基本類型組合成複雜類型。
變量類型
以下類型被叫做值類型,因為這些類型的變量總是要被指派,作為函數參數或者在指派中,總需要拷貝。
布爾類型
布爾:可能的常量值 是 真或假
操作符:
- !(邏輯非)
- &&(邏輯與,“and”)
- || (邏輯或,”or“)
- ==(相等)
- !=(不等)
- 操作符||和&&可以應用正常短路規則,即 表達式 f(x) || g(y), 如果f(x) 已是真,g(y)将不用計算,即使它有副作用 (真||任意值 均為真,假&&任意布爾值 均為假)。
整型
int• / uint•: 是有符号和無符号的整數,關鍵字uint8 到 uint256 步長8 (從8到256位的無符号整數 )
uint 和 int 分别是 uint256 和 int256的别名
- 比較 : <=,<,==,!=,>=,> (計算布爾量)
- 位操作符: &,|,^(位異或),~(位取反)
- 算術操作符:+,-,一進制-,一進制+,,/,%(取餘數),*(幂次方)
位址
位址: 20位元組(一個 Ethereum位址),位址的類型也可以有成員(請看位址功能 (#functions-on-addresses)) 作為所有合約的base
- <=, <, ==, !=, >= 和 >
位址成員:
- 賬戶餘額(balance)和發送(send)
若查詢到有資産餘額的位址,然後發送 Ether(以wei為機關) 到send 函數的位址上
address x = 0x123;
address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.send(10);
注解
如果x是合約位址,它的代碼(特别的指出 如果有回退函數,) 将和send 調用一起執行(這是EVM的限制,不能修改) 如果用完了gas或者失敗,Ether轉移将被回退,這種情況下,send傳回false
- 調用(call)和調用碼( callcode)
另外,和合約的接口不是附在ABI 上,函數調用可以引用任意數量的參數,這些參數要填補成32位元組,并被拼接。一個例外的情況是第一個參數被确切的編碼成4位元組,這種情況下,不用填補,直接使用函數符号
address nameReg = 0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2;
nameReg.call("register", "MyName");
nameReg.call(bytes4(sha3("fun(uint256)")), a);
函數調用傳回了一個布爾值,表示函數是否是正常調用結束(true)或引起了EVM異常(false)。不可能通路傳回實際資料(這個我們需要提前知道編碼和大小)。
同樣,可以使用函數callcode:不同之處在于,隻使用給定位址的編碼,所有其他方面(存儲、餘額…)取自于目前的合約。callcode的目的是使用庫代碼存儲在另一個合同。使用者必須确儲存儲在兩個合約的布局适用于callcode。
call和callcode是非常低級的函數,它可以作為打破Solidity的類型安全的最後手段。
- call and callcode
請注意
所有合同繼承成員位址,是以可以使用this.balance查詢目前合約的餘額。
Fixed-size byte arrays
bytes1, bytes2, bytes3, ..., bytes32. byte is an alias for bytes1.
Operators:
- Comparisons: <=, <, ==, !=, >=, > (evaluate to bool)
- Bit operators: &, |, ^ (bitwise exclusive or), ~ (bitwise negation)
固定大小的位元組數組
bytes1, bytes2, bytes3, …, bytes32. byte 都是 bytes1的别名.
- 比較符 : <=, <, ==, !=, >=, > (布爾的評估)
- 位操作符;: &, |, ^ (按位置異或),~(按位取反)
動态配置設定大小位元組數組:
bytes:動态配置設定大小位元組數組,參看
Arrays,不是一個值類型!
string:動态大小UTF8編碼的字元串,參看
。不是一個值類型!
作為一個常識, 使用bytes來表示任意長度原始位元組資料,使用string來表示特定長度字元串資料(utf - 8編碼)。如果你想限定特定數量的位元組長度, 就使用bytes1 到 bytes32, 因為這樣占用的存儲空間更少。
整型常量
整型常量是特定精度整數,它們也可以和非常量同時使用。例如, var x = 1 - 2; 1 - 2的值是-1,然後指派給x, 這時x接收類型為int8——最小的類型,其中包含-1, 雖然1和2的類型實際上是uint8。
有時最大超過256位的整型常量也可用于計算:var x =(0 xffffffffffffffffffff * 0 xffffffffffffffffffff)* 0;這裡,x的值是0,它的類型是uint8類型。
字元串常量
字元串常量用兩個雙引号括起來(“abc”)。和整型常量相比,字元串常量有些不同,字元串常量可以隐式轉換成bytes • 如果合适,可以是bytes,也可以是string。
contract test {
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
ActionChoices choice;
ActionChoices constant defaultChoice = ActionChoices.GoStraight;
function setGoStraight()
{
choice = ActionChoices.GoStraight;
}
枚舉是一種Solidity中的建立一個使用者定義的類型。枚舉類型中的枚舉值可顯式轉換,但從整數類型隐式轉換是不允許的。
contract test {
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
ActionChoices choice;
ActionChoices constant defaultChoice = ActionChoices.GoStraight;
function setGoStraight()
{
choice = ActionChoices.GoStraight;
}
// Since enum types are not part of the ABI, the signature of "getChoice"
// will automatically be changed to "getChoice() returns (uint8)"
// for all matters external to Solidity. The integer type used is just
// large enough to hold all enum values, i.e. if you have more values,
// `uint16` will be used and so on.
/ /因為枚舉類型不是ABI的一部分,“getChoice”的符号
/ /将自動改為“getChoice()傳回(uint8)”
/ /從Solidity外部看,使用的整數類型
/ /足夠容納所有枚舉值,但如果你有更多的值,
/ /“uint16”将使用。
function getChoice() returns (ActionChoices)
{
return choice;
}
function getDefaultChoice() returns (uint)
{
return uint(defaultChoice);
}}
引用類型
複雜類型,例如類型并不總是适合256位,比我們已經看到的值類型更複雜的類型,必須更仔細地處理。因為複制拷貝他們可能相當耗費存儲和時間, 我們必須考慮把它們存儲在記憶體(這不是持久化)或者存儲器(狀态變量存放的地方)。
資料位置
每一個複雜類型,即數組和結構體,有一個額外的注解,“資料位置”,不管它是存儲在記憶體中,還是存儲在存儲器上。根據上下文,總是有一個預設的,但它可以通過附加存儲或記憶體覆寫類型。函數參數的預設值(包括傳回參數)是在記憶體上,局部變量的預設存儲位置是在存儲器上。存儲器上存有狀态變量(很明顯)。
(除了記憶體,存儲器這兩個位置之外),還有第三個資料位置,“calldata”,這是一個 無法改變的,非持久的 存儲函數參數的地方。外部函數的函數參數(不傳回參數)“calldata”,其在形式上象記憶體。
資料位置很重要,因為它們改變指派方式:在存儲和記憶體以及狀态變量之間指派(甚至從其他狀态變量)總是建立一個獨立的副本。指派隻配置設定一個本地存儲變量引用,這總是指向狀态變量的引用,後者同時改變。另一方面,從一個記憶體存儲引用類型, 指派到另一個記憶體存儲引用類型,(這時)并不建立一個副本。
contract c {
uint[] x; // the data location of x is storage
// the data location of memoryArray is memory
function f(uint[] memoryArray) {
x = memoryArray; // works, copies the whole array to storage
var y = x; // works, assigns a pointer, data location of y is storage
y[7]; // fine, returns the 8th element
y.length = 2; // fine, modifies x through y
delete x; // fine, clears the array, also modifies y
// The following does not work; it would need to create a new temporary /
// unnamed array in storage, but storage is "statically" allocated:
// y = memoryArray;
// This does not work either, since it would "reset" the pointer, but there
// is no sensible location it could point to.
// delete y;
g(x); // calls g, handing over a reference to x
h(x); // calls h and creates an independent, temporary copy in memory
}
function g(uint[] storage storageArray) internal {}
function h(uint[] memoryArray) {}}
contract c {
uint[] x; // the data location of x is storage x的資料位置是存儲器
// the data location of memoryArray is memory memoryArray的資料位置是記憶體
function f(uint[] memoryArray) {
x = memoryArray; // works, copies the whole array to storage 運作,拷貝整個數組到存儲器
var y = x; // works, assigns a pointer, data location of y is storage 運作,指派到一個指針,y的資料位置是存儲器
y[7]; // fine, returns the 8th element 好了,傳回第8個元素
y.length = 2; // fine, modifies x through y 好了,通過y改變x
delete x; // fine, clears the array, also modifies y 好了,清除數組,也改變y
// The following does not work; it would need to create a new temporary / 以下代碼不起作用, 它是在存儲中創立一個臨時的未命名的數組,但存儲器是“靜态”配置設定的
// unnamed array in storage, but storage is "statically" allocated:
// y = memoryArray;
// This does not work either, since it would "reset" the pointer, but there 這個也不起作用,因為 它重置了指針, 但已經沒有相應的位置可以指向
// is no sensible location it could point to.
// delete y;
g(x); // calls g, handing over a reference to x 調用g(x) 将x作為引用
h(x); // calls h and creates an independent, temporary copy in memory 調用h(x). 在記憶體中創立了一個獨立的,暫時的拷貝
}
function g(uint[] storage storageArray) internal {}
function h(uint[] memoryArray) {}}
總結
強制資料位置:
- 外部函數的參數(不傳回):calldata
- 狀态變量:存儲器
預設資料位置:
- 函數(有傳回)的參數:記憶體
- 其他所有局部變量:存儲器
數組
數組是可以在編譯時固定大小的,也可以是動态的。對于存儲器數組來說,成員類型可以是任意的(也可以是其他數組,映射或結構)。對于記憶體數組來說 ,成員類型不能是一個映射;如果是公開可見的函數參數,成員類型是必須是ABI類型的。
固定大小k的數組和基本類型T,可以寫成T[k], 動态數組寫成 T[ ] 。例如, 有5個基本類型為uint 的動态數組的數組 可以寫成uint[ ][5] ( 注意,和一些其他語言相比,這裡的符号表示次序是反過來的)。為了通路第三動态數組中的第二個uint, 必須使用x[2][1](下标是從零開始的,通路模式和聲明模式正好相反, 即x[2]是從右邊剔除了一階)。
bytes和 string 是特殊類型的數組。 bytes 類似于byte[ ],但它是緊湊排列在calldata裡的。string 等于 bytes , 但不允許用長度或是以索引通路(現在情況是這樣的)。
是以bytes應該優先于byte[ ],因為它效率更高。
如果你想通路字元串s的某個位元組, 要使用 bytes(s).length/bytes(s)[7]= ’ x ‘;。記住,你正在通路的低級utf - 8位元組表示,而不是單個字元!
成員(函數):
length: 總有 一個稱作length的成員(函數)來存放元素的數量。動态數組可以通過改變.length成員(函數),在存儲器裡來調整大小(不是在記憶體中)。當試圖通路現有長度之外的成員時,這并不是自動被許可的。(數組)一旦建立,記憶體裡的數組大小是固定的(如果是動态的數組,則取決于運作時參數)。
push :動态存儲數組arrays和位元組bytes(不是字元串string)有一個成員函數稱作push,可在數組的尾部添加一個元素。函數傳回新的長度。
警告
到目前為止,還不可以在外部函數中使用數組的數組。
由于EVM的局限,不可能從外部函數調用傳回的動态内容。合約函數f contract C { function f() returns (uint[]) { … } } 使用web3.js調用,将有傳回值, 但使用Solidity調用,就沒有傳回值。
現在唯一的解決方法是使用較大的靜态尺寸大小的數組。
contract ArrayContract {
uint[2*\*20] m_aLotOfIntegers;
// Note that the following is not a pair of arrays but an array of pairs.
bool[2][] m_pairsOfFlags;
// newPairs is stored in memory - the default for function arguments
function setAllFlagPairs(bool[2][] newPairs) {
// assignment to a storage array replaces the complete array
m_pairsOfFlags = newPairs;
}
function setFlagPair(uint index, bool flagA, bool flagB) {
// access to a non-existing index will throw an exception
m_pairsOfFlags[index][0] = flagA;
m_pairsOfFlags[index][1] = flagB;
}
function changeFlagArraySize(uint newSize) {
// if the new size is smaller, removed array elements will be cleared
m_pairsOfFlags.length = newSize;
}
function clear() {
// these clear the arrays completely
delete m_pairsOfFlags;
delete m_aLotOfIntegers;
// identical effect here
m_pairsOfFlags.length = 0;
}
bytes m_byteData;
function byteArrays(bytes data) {
// 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) {
// Dynamic memory arrays are created using `new`:
uint[2][] memory arrayOfPairs = new uint[2][](size);
// Create a dynamic byte array:
bytes memory b = new bytes(200);
for (uint i = 0; i < b.length; i++)
b[i] = byte(i);
return b;
}}
contract ArrayContract {
uint[2\20] m_aLotOfIntegers;
// Note that the following is not a pair of arrays but an array of pairs. 注意下面不是兩個數組,而是一個數組,該數組的成員是一對值
bool[2][] m_pairsOfFlags;
// newPairs is stored in memory - the default for function arguments newPairs在記憶體中存儲-這是函數參數的預設方式
function setAllFlagPairs(bool[2][] newPairs) {
// assignment to a storage array replaces the complete array 指派到一個存儲器數組裡以替換整個數組
m_pairsOfFlags = newPairs;
}
function setFlagPair(uint index, bool flagA, bool flagB) {
// access to a non-existing index will throw an exception
m_pairsOfFlags[index][0] = flagA;
m_pairsOfFlags[index][1] = flagB;
}
function changeFlagArraySize(uint newSize) {
// if the new size is smaller, removed array elements will be cleared 如果新的尺寸太小,則已經移除的元素将被清除
m_pairsOfFlags.length = newSize;
}
function clear() {
// these clear the arrays completely
delete m_pairsOfFlags;
delete m_aLotOfIntegers;
// identical effect here
m_pairsOfFlags.length = 0;
}
bytes m_byteData;
function byteArrays(bytes data) {
// byte arrays ("bytes") are different as they are stored without padding, 如果沒有填充的話,位元組數組("bytes")和存儲時是不同的
// but can be treated identical to "uint8[]" 但可以轉換成 "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) {
// Dynamic memory arrays are created using `new`: 使用`new`創立動态記憶體數組
uint[2][] memory arrayOfPairs = new uint[2][](size);
// Create a dynamic byte array: 創立動态 byte 數組
bytes memory b = new bytes(200);
for (uint i = 0; i < b.length; i++)
b[i] = byte(i);
return b;
}}
結構體
Solidity 提供了一種方法來定義新類型的形式結構,如下面的例子所示:
contract CrowdFunding {
// Defines a new type with two fields.
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 is return variable
// Creates new struct and saves in storage. We leave out the mapping type.
campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);
}
function contribute(uint campaignID) {
Campaign c = campaigns[campaignID];
// Creates a new temporary memory struct, initialised with the given values
// and copies it over to storage.
// Note that you can also use Funder(msg.sender, msg.value) to initialise.
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;
c.beneficiary.send(c.amount);
c.amount = 0;
return true;
}}
contract CrowdFunding {
// Defines a new type with two fields. 定義了兩個域的新類型
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 is return variable campaignID是傳回的變量
// Creates new struct and saves in storage. We leave out the mapping type. 建立一個新的結構體,儲存在存儲器裡, 保留了映射類型
campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);
}
function contribute(uint campaignID) {
Campaign c = campaigns[campaignID];
// Creates a new temporary memory struct, initialised with the given values 建立了一個新的臨時記憶體結構體,用給定的值進行初始化
// and copies it over to storage. 拷貝到存儲器上
// Note that you can also use Funder(msg.sender, msg.value) to initialise. 注意你可以使用 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;
c.beneficiary.send(c.amount);
c.amount = 0;
return true;
}}
此(例子)合約沒有提供衆籌合約的完整功能, 但它包含了必要的基本概念,以便(讓我們更好地)了解結構體。結構體類型可以是内部映射或者是數組,他們本身也可以包含映射和數組。
通常這是不可能,即一個結構體包含一個自身類型的成員, 雖然結構體本身可以是一個映射的值類型成員。這個限制是必要的, 原因是結構體的大小是有限的。
注意所有的函數中, 結構類型是指派給一個局部變量(預設存儲資料的位置)。這并不複制結構體,僅僅儲存了一個引用, 本地變量的指派最終還是以寫進了狀态中。
當然,您也可以直接通路結構體的成員變量,而不用指派到一個局部變量,如campaigns[campaignID].amount = 0.
映射
映射類型被聲明為 mapping _KeyType => _ValueType, _KeyType可以是除了映射以外的其他任何類型,_ValueType可以是任何類型,包括映射。
映射可以被視為初始化的散清單,這樣每一個鍵值都存在, 這些鍵值在位元組表示上是全零。相似性到此為止,盡管:key資料實際上并不是存儲在一個映射中,它隻有在使用sha3哈希查找值使用。
是以,映射沒有長度,也沒有一個鍵或值的被“set”的概念。
映射是隻允許為狀态變量(在内部函數中作為一個存儲引用類型)。
包括左值操作的操作符
如果是一個左值操作(即一個可以指派給它的變量),可以使用以下的操作符:
a += e相當于 a = a + e。操作符- = * =,/ = % = | = & = ^ = 都有相應的定義。a++和a–相當于a+ = 1 /a - = 1,但是表達式本身還有一個操作前的值。相比之下, –a和++a有相同的影響但傳回值改變。
删除
删除一個指定類型的初始值為整數,即相當于a= 0,但是它也可以用于數組,它配置設定一個動态數組的長度為零或一個靜态數組長度相同的所有元素重置。對于結構體,它配置設定一個struct,重置所有成員。
删除沒有影響整體映射(如映射的鍵可能是任意的,通常是未知的)。如果你删除一個結構,它将重置沒有映射的所有成員,也可以是遞歸的成員,除非它們映射。然而,個别鍵和他們的映射是可以删除。
重要的是要注意,删除一個a的指派, 即它存儲在一個新的對象。
contract DeleteExample {
uint data;
uint[] dataArray;
function f() {
uint x = data;
delete x; // sets x to 0, does not affect data
delete data; // sets data to 0, does not affect x which still holds a copy
uint[] y = dataArray;
delete dataArray; // this sets dataArray.length to zero, but as uint[] is a complex object, also
// y is affected which is an alias to the storage object
// On the other hand: "delete y" is not valid, as assignments to local variables
// referencing storage objects can only be made from existing storage objects.
}}
contract DeleteExample {
uint data;
uint[] dataArray;
function f() {
uint x = data;
delete x; // sets x to 0, does not affect data 設定x為0, 不影響data
delete data; // sets data to 0, does not affect x which still holds a copy 設定data為0,x不受影響,x仍然有一個拷貝
uint[] y = dataArray;
delete dataArray; // this sets dataArray.length to zero, but as uint[] is a complex object, also dataArray.length長度是0。但是uint[ ]是一個複雜對象, y受影響,其是存儲對象的别名
// y is affected which is an alias to the storage object
// On the other hand: "delete y" is not valid, as assignments to local variables 另外, "delete y"是非法的,因為y是指派到本地變量
// referencing storage objects can only be made from existing storage objects.引用存儲對象僅僅來自于現有的存儲對象
}}
基本類型之間的轉換
隐式轉換
如果一個操作符應用于不同類型, 編譯器(就會)試圖隐式把操作數的類型,從一種類型轉換到其他類型(指派也是如此)。一般來說,一個隐式的值類型之間的轉換是可能的,如果是語義敏感的話,資訊不會丢失:unt8可轉換成uint16, int128, int256, 但int8不能轉換成uint256(因為uint256放不下 如 -1)。此外,無符号整數可以轉換成相同或更大的尺寸的bytes , 但反過來,不行 。任何類型都可以轉化為uint160,也可以轉換為位址。
顯式轉換
如果編譯器不允許隐式轉換,但你知道你在做什麼,一個顯式的類型轉換有時是可能的:
int8 y = 3;
uint x =uint(y);
這個代碼片段結尾 , x的值是 0xfffff . .fd(64個十六進制字元),-3在256位的二進制補碼表示。
如果一個類型是顯式地轉換為一個更小的類型,高階位将被移除。
uint32 = 0x12345678;
uint16 b = uint16(a);/ / *b will be 0x5678 now* *b現在變成了0x5678,(少了1234)*
類型推導
為友善起見,它并不總是必須顯式地指定一個變量的類型,編譯器會自動從第一個指派表達式的變量類型裡推斷出新變量的類型:
uint20 x = 0 x123;
var y = x;
在這裡,y的類型是uint20。在函數參數或傳回參數是不可能使用var(這個關鍵字)的。
這個類型僅僅是從第一次指派推導得出的,是以以下代碼片段的循環是無限的, 因為 i 的類型是uint8, 這種類型的任何值都小于2000。for (var i = 0;< 2000;i+ +){…}
NextPrevious
© Copyright 2015, Ethereum. Revision 9b9d10b4.
Built with
Sphinxusing a
themeprovided by
Read the Docs.
Read the Docsv: latest
轉自:
https://github.com/twq0076262/solidity-zh/edit/master/types.md如果你希望高效的學習以太坊DApp開發,可以通路彙智網提供的最熱門線上互動教程:
其他更多内容也可以通路
這個以太坊部落格。