Solidity語法知識點(文末有彩蛋)
最近報名參加了矽谷區塊鏈舉辦的《智慧合約開發課》第二期培訓班,根據培訓要求,不能透露課程的內容,但我會在steemit上不斷地記錄我的成長過程。
https://steemit.com/cn/@speeding/smart-contract-development0
單純地學Solidity的語法知識點非常枯燥,而放在一個個的實際例子中逐步深入時,則輕鬆了許多,當前已經學完了智慧合約開發課的第三課,必須抓緊把學過的語法點總結一下。
一、檔名
solidity檔案的副檔名為*.sol
二、指定編譯器版本
pragma solidity ^0.4.0;
表示源程式在大於等於0.4.0版本的編譯器可以正常工作,在大於等於0.5.0版本中的編譯器中無法工作。即:
0.4.0 <= version < 0.5.0
關於pragma的詳細文件在這裡:http://solidity.readthedocs.io/en/develop/layout-of-source-files.html#version-pragma
而版本號之前的“^”符號的含義,來自於npm中的語法 。
三、資料型別
solidity是靜態型別語言,所有變數需要有定義宣告。
1)整數
常用的無符號整數型別有uint8, uint16, uint24, ... ,uint256。256個位元組的無符號整數uint256可以簡寫為uint。而有符號的整數則從int8, int16一直到int256。
這些整數經常會用來儲存使用者的token數量,小心加、減、乘、除運算後的結果溢位,那可是非常慘痛的損失。
uint a = 365;
2)地址
address用來儲存以太坊的地址,實際上就是不超過20位元組的無符號整數,例如:
address a = 0xdd870fa1b7c4700f2bd7f44238821c26f7392148;
後面的0x開頭的一串十六進位制數並不是字串,不需要雙引號。而在remix除錯程式時,傳入的地址引數卻需要雙引號括起來,新手一開始經常會遇到這個錯誤。
地址有合約地址和普通的錢包地址兩種。
3)結構struct
與C語言非常相似,不用多說。
struct Participator { address addr; uint amount; }
4)陣列
Solidity支援定長陣列:
uint[5] a;
也支援動態陣列:
uint []b;
在動態陣列中增加一個元素用push()函式。
b.push(1);
b.push(2);
用b.length
得到陣列的長度,還可以直接修改length來刪除元素。
b.length = 1;
5)mapping型別
這種型別相當於其它語言中的雜湊表,一開始不太適應,是solidity中非常重要的一種資料型別,以後再展開。
6)var
var並不是表示動態型別,而是讓書寫更簡單,一個值在分配給var變數時,其型別就已經確定了。如果要賦值給其它型別,仍要進行強制型別轉換。
7)其它型別
solidity中還支援布林型別、字串型別、列舉型別等等。非常神奇,這次的培訓課中竟然一直沒講string型別,通常的程式語言都會在第一課介紹"hello world"。可能智慧合約是與token打交道,而不是輸出字串吧。
四、函式修飾符
function modifier可以讓函式顯得更加簡潔,比如經常判斷一個函式的msg.sender是不是合約構建者時,不需要頻繁插入require(msg.sender == owner);
這樣的語句,只需要定義一個modifier。
modifier onlyOwner {
require(msg.sender == owner);
_;
}
而在函式宣告的主體尾部加上onlyOwner就可以了。上面的語法中最有意思的是_;
這行語句,表示原來函式中的所有語句。當函式中含有return()語句時,替代規則有點特殊。
function test() onlyOwner {
// ...
}
payable實際是一個內建的修飾符,表示一個函式在呼叫時要傳送ether。
五、繼承
solidity支援多重繼承,繼承線採用與Python相似的C3 Linearization規則。
contract parent {
// ...
}
contract child is parent {
// ...
}
抽象合約是函式只有宣告,沒有具體的實現。
interface與java語言的語法類似,不用多說。
六、多引數返回
在函式的返回值中可以一次返回多個引數,比如:在返回一個數組中的元素的同時,返回它在陣列中所在的位置時,這樣可以一次給多個變數賦值。
function test() returns (address item, uint index) {
address []arr;
// ...
return (arr[1], 1);
}
address owner;
uint i;
(owner, i) = test();
(owner, ) = test(); //如果不需要第2個引數
七、異常處理
在比較早的solidity版本中都用throw(),現在統統用revert(),可以保證在遇到異常時,回滾到呼叫前的狀態。用require可以寫得更簡練。
require(msg.sender == owner);
另外的一個容易讓人搞糊塗的語句是assert,在C語言中,assert翻譯為“斷言”,這類語句只在除錯時起作用,用來排查軟體的重大BUG,這裡也是類似。表示程式在執行到這條語句時,肯定會滿足其中的情況。如果有異常發生,說明軟體肯定有重大的BUG,由於solidity中涉及到轉帳等重要操作,assert失敗後,會耗光所有的GAS,讓交易失敗,防止更嚴重的事情發生。assert常用於陣列越界、元素非空的檢查上。
而require要檢查的是軟體可能經常發生的情況,比如給函式中傳遞的引數時是否滿足一定的條件等等。
八、幾個全域性變數
solidity中內建了msg,block和tx這幾個全域性變數。
- msg.value,訊息所附帶的貨幣量,單位為wei
- msg.sig,呼叫資料的前四個位元組,函式識別符號
- msg.sender,當前呼叫發起人的地址
- msg.gas,當前剩餘的gas
- block.difficulty,當前區塊的難度值
- block.blockhash(),某個區塊的雜湊值
- block.coinbase,當前區塊礦工的地址
- block.gaslimit,當前區塊的gas上限
- block.number,當前區塊的序號
- block.timestamp,當前區塊的時間戳,是uint型別
- now,等同於block.timestamp
- tx.gasprice,交易的gas價格
- tx.origin,交易的發起人,完整的呼叫鏈
九、可見性
函式的可見性有external、public、internal和private。
狀態變數的可見性有public、internal和private,類似於C++語言中的public、protected和private。
external只能修飾函式,說明這個函式只能被外部合約呼叫。假設函式f()是external,還想在合約內呼叫,可以用this.f()。
十、delete
delete操作可以用於任何變數,將其設定為預設值0。
對可變陣列使用delete,會刪除所有元素,其長度變為0。
對定長陣列使用delete,則會重置所有元素為0,也可以重置指定位置的元素。
對map型別使用delete,什麼也不會發生。
對map型別的一個鍵使用delete,則會刪除與該鍵相關的值。
我學習合約程式設計的目的是什麼呢?
1、手工用imtoken錢包給許多人發代幣是一件非常費力且容易出錯的事,想寫一個合約來自動完成這件事。
2、深入瞭解以太坊的背後原理。
3、智慧合約在寫作社群激勵等很多地方有廣闊的應用前景,程式設計師學會它肯定是站在區塊鏈的頂端,既有趣又賺錢。