Solidity值型別與各種圖解
區塊鏈中級.以太坊開發
From:JamesZou & 傳智播客研究院 & 傳智播客區塊鏈
一.值型別和引用型別
1.值型別
- 值型別 是指變數在賦值過程中是將資料完整的拷貝一份,再賦值給新的變數,這種方式需要開闢新的記憶體空間,效率較低,兩個變數完全獨立,修改一個不會影響另外一個。
- 布林(Booleans)
- 整型(Integer)
- 地址(Address)
- 定長位元組陣列(fixed byte arrays)
- 有理數和整型(Rational and Integer Literals,String literals)
- 列舉型別(Enums)
- 函式(Function Types)
思考:下面這個程式碼中的 luckNum 最後等於多少?
pragma solidity ^0.4.25;
contract ValueType{
function testValueType() public view returns(uint){
uint luckNum = 101;
uint loveNum = luckNum;
changeValue(luckNum);
return luckNum;
}
function changeValue(uint _num) private{
_num += 100;
}
}
2.引用型別
- solidity沒有指標,對於複雜的結構進行高效傳遞方式是使用關鍵字
storage
進行修飾。 - 複雜型別,佔用空間較大的。在拷貝時佔用空間較大。所以考慮通過引用傳遞。常見的引用型別有:
- 字串(string)
- 不定長位元組陣列(bytes)
- 陣列(Array)
- 結構體(Structs)
二.資料型別
1.值型別詳介
1.1 布林
bool flag1 = true;
bool flag2 = false;
1.2 整型
-
int(有符號整型,有正有負)
-
uint(無符號整型,無負數)
-
以8位為區間,支援int8,int16,int24 至 int256,uint同理。
int預設為int256,uint預設為uint256
function intTest() public returns(int){
int8 i = 10;
int j = 10;
return i + j;
}
1.3 地址
1.3.1 概念
- 以太坊地址的長度,大小
20個位元組
,每個位元組8位,20 * 8 =160位
,所以可以用一個uint160
編碼。地址是所有合約的基礎,所有的合約都會繼承地址物件,通過合約的地址串,呼叫合約內的函式。
1.3.2 運算子
描述 | 符號 |
---|---|
比較運算子 | <=,<,==,!=,>=,> |
1.3.3 操作
屬性/方法 | 含義 | 備註 |
---|---|---|
balance(uint256) | 獲取餘額(wei) | 屬性,其餘的都是方法 |
transfer(uint256 value) | 給 address 轉賬 value(Wei),失敗會拋異常 | 建議使用 |
send(uint256 value) | 和 transfer 類似,transfer 更常用,失敗會返回false | 不建議使用 |
call | 合約內部呼叫合約 | |
delegatecall | 調底層程式碼,別用 | |
callcode | 調底層程式碼,別用 |
- call 是一個底層的介面,用來向一個合約傳送訊息,也就是說如果你想實現自己的訊息傳遞,可以使用這個函式。函式支援傳入任意型別的任意引數,並將引數打包成32位元組,相互拼接後向合約傳送這段資料。
pragma solidity ^0.4.25;
contract Lover{
string outerMsg;
function(){
outerMsg = string(msg.data);
}
function getFail() returns (string){
return outerMsg;
}
}
contract CallLover{
function callData(address addr) returns (bool){
return addr.call("i love Ether ~~!");
}
}
- call與delegatecall的功能類似,區別僅在於後者僅使用給定地址的程式碼,其它資訊則使用當前合約(如儲存,餘額等等)
注意:在 solidity 原始碼中,address 不需要加雙引號。但在 Remix 的對話介面中輸入 address 時,務必加上雙引號,否則會報錯
1.3.4 餘額
- 返回指定地址的餘額
pragma solidity ^0.4.25;
contract addressTest{
function getBalance(address addr) constant public returns (uint){
return addr.balance;
}
}
-
合約地址 (this)
如果只是想返回當前合約賬戶的餘額,可以使用
this
指標,this
表示合約自身的地址
pragma solidity ^0.4.0;
contract addressTest{
function getBalance() constant public returns (uint){
//return addr.balance;
return this.balance; // <<----此處使用this代替
}
}
-
轉賬(send,transfer)
send和transfer函式提供了由合約向其他地址轉賬的功能。
對比項 | send | transfer | 備註 |
---|---|---|---|
引數 | 轉賬金額 | 轉賬金額 | wei單位 |
返回值 | true/false | 無(出錯拋異常) | transfer更安全 |
pragma solidity ^0.4.25;
contract TransferTest {
function transfer123(address addr) payable {
addr.transfer(msg.value);
}
}
- call 方法 (略)
1.4 位元組陣列
1.4.1 定長位元組陣列
-
solidity內建了一些陣列的資料型別:(和go語言做一下對比, var [8] byte),完全只讀
bytes1
, … ,bytes32
,允許值以步長1遞增。- byte預設表示bytes1,byte是型別,bytes是型別,bytes1是內建陣列
- bytes1只能儲存1個位元組,即8位的內容,bytes2最多隻能儲存2個位元組,即16位的內容。以此類推…
- 長度可以讀取 length
- 長度不可以修改
- 可以通過下標訪問
- 內容不可修改
-
內建方法:length() -> 返回陣列長度
-
儲存方式:16進位制ascii碼
pragma solidity ^0.4.25;
//bytes1
contract fixedArray {
/*
1. 長度可以讀取 length
2. 長度不可以修改
3. 可以通過下標訪問
4. 內容不可修改
*/
//bytes1 ... bytes32
//bytes1 b1 = "xy";
bytes2 b2 = "xy";
bytes3 public b3 = "xy";
uint public len = b3.length;
//b3.length = 10;
bytes8 b8 = "12345678";
//b8_0返回0x31,即十進位制的數字1的ascii值(3*16+1=49)
bytes1 public b8_0 = b8[0];
//b = "HELLO";ERROR,定義之後不可修改
//b8[1] = "0";
//b8= "4567";
}
- 運算子支援
描述 | 符號 |
---|---|
比較運算 | <=,<,==,!=,>=,> |
位運算子 | &,|,^(異或),~非 |
下標訪問 | [0,n),n表示長度 |
1.4.2 不定長位元組陣列
-
bytes:不定長度的位元組陣列 (Dynamically-sized byte array)
bytes型別可以使用 push() 方法,具有 length 屬性
string public name = "itheima.com";
bytes public g = 0x6c697975656368756e;
// 初始化一個兩個位元組空間的位元組陣列
bytes public name = new bytes(2);
- string: 動態長度的UTF-8編碼的字元型別(非值型別),它其實是一個特殊的可變位元組陣列
string public str01 = "itheima.com";
- solidity中字串不像JS中那樣,沒有length()方法,須轉換成位元組陣列才能使用length屬性來獲得長度
function getStrLegnth(string _str) constant returns (uint) {
return bytes(_str).length; // 強制轉換string為bytes
}
- 一個好的使用原則是:
bytes用來儲存任意長度的位元組資料,string用來儲存任意長度的UTF-8編碼 的字串資料。
如果長度可以確定,儘量使用定長的如byte1到byte32中的一個,因為這樣更省空間。
1.5 列舉型別
- 列舉型別是在Solidity中的一種使用者自定義型別。
- 列舉可以顯示的轉換與整數進行轉換,但不能進行隱式轉換。顯示的轉換會在執行時檢查數值範圍,如果不匹配,將會引起異常。
- 列舉型別應至少有一名成員,列舉元素預設為uint8,當元素數量足夠多時,會自動變為uint16,第一個元素預設為0,使用超出範圍的數值時會報錯。
pragma solidity ^0.4.0;
contract test {
enum WeekDays {
Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}
WeekDays currentDay;
WeekDays defaultday = WeekDays.Sunday;
function setDay(weekDays _day) public {
currentDay = _day;
}
function getDay() public view returns(uint256) {
return uint256(currentDay);
}
function getDefaultDay() public view returns(uint256) {
return uint256(defaultday);
}
}
1.6 函式型別
1.6.1 函式修飾符
- 函式型別也就是我們所說的函式,本身也是一個特殊的變數,它可以
當做變數賦值
,當做函式引數傳遞
,當做返回值
。
修飾符 | 說明 |
---|---|
public | 公有,任何人(擁有以太坊賬戶的)都可以呼叫 |
private | 私有, 只有智慧合約內部可以呼叫 |
external | 僅合約外部可以呼叫,合約內部需使用this呼叫 |
internal | 僅合約內部和繼承的合約可以呼叫 |
view/constant | 函式會讀取但是不會修改任何contract的狀態變數 |
pure | 函式不使用任何智慧合約的狀態變數 |
payable | 呼叫函式需要付錢,錢付給了智慧合約的賬戶 |
returns | 返回值函式宣告中使用 |
注意,所有在合約內的東西對外部的觀察者來說都是可見,將某些東西標記為private
僅僅阻止了其它合約來進行訪問和修改,但並不能阻止其它人看到相關的資訊。
1.6.2 匿名函式
-
一個合約可以有且只有一個匿名函式,此函式不能有引數,也不能有任何返回值,當我們企圖去執行一個合約上沒有的函式時,那麼合約就會執行這個匿名函式。
當合約在只收到以太幣的時候,也會呼叫這個匿名函式,而且一般情況下會消耗很少的gas,所以當你接收到以太幣後,想要執行一些操作的話,你儘可以把你想要的操作寫到這個匿名函式裡,因為這樣做成本非常便宜。
//如果想向合約轉賬,在合約中新增如下函式即可
function() payable {
//函式體什麼都不填
}