參考教程:基于以太坊的智能合約開發教程【Solidity】_嗶哩嗶哩_bilibili
1、第一個程序——Helloworld:
//聲明版本號(程序中的版本號要和編譯器版本號一致)
pragma solidity ^0.5.17;
//合約
contract HelloWorld
{//合約屬性變量,也叫狀態變量(定義方式:數據類型 變量名 = 數據)string myName = "helloworld"; //在solidity中,用單引號包含字符串也是可以的//合約中的方法(注意語法順序,其中此處“view”代表方法只讀,不會消耗燃料;“returns”后的是返回值類型)function getName() public view returns(string){return myName; //返回值類型要與returns聲明的嚴格相同}//可以修改屬性變量的值,但是會消耗燃料function changeName(string _newName) public{myName = _newName;}//“pure”代表不能讀取也不能改變狀態變量function pureName(string _name) public pure returns(string){return _name;}/*用constant、view、pure修飾function分別表示:constant:只能讀取不可改變狀態變量(就是contract中定義的變量)view:只能讀取不可改變狀態變量,和constant一樣pure:不能讀取也不能改變狀態變量*/}
(1)程序編譯完成后,需要在虛擬機上運行,將合約部署好后便可執行剛剛編寫的函數。(注意,合約一旦部署,就會永久存在于區塊鏈上,且不可篡改,不過可以銷毀)
(2)執行完成后,可以得到以下交易信息,下圖所示的就是生成的區塊信息:
(3)代碼相關:
①屬性變量定義方式:數據類型 變量名 = 數據(變量命名規則和c語言相同)
②一般用雙引號包含一串字符串,不過在solidity中,用單引號包含字符串也是可以的。
③Solidity值類型:(在Solidity中,一旦一個變量被指定了某個數據類型,如果不經過強制轉換,那么它就永遠是這個數據類型,在調用函數時如果形參和實參類型不同將會報錯)
? 布爾類型(bool):可能的取值為字符常量值true或false
? 整型(int/uint):分別表示有符號和無符號的不同位數的整型變量,支持關鍵字uint8到 uint256(無符號,從8位到256位)以及int8到int256,以8位為步長遞增
? 定長浮點型(fixed / ufixed):表示各種大小的有符號和無符號的定長浮點型,在關鍵字ufixedMxN和fixedMxN中,M表示該類型占用的位數,N表示可用的小數位數
? 地址(address):存儲一個 20 字節的值(以太坊地址大小)
? 定長字節數組:關鍵字有 bytes1,bytes2,bytes3,...,bytes32
? 枚舉(enum):一種用戶可以定義類型的方法,與C語言類似,默認從0開始遞增,一般用來模擬合約的狀態
? 函數(function):一種表示函數的類型
④solidity函數的標準形式:
function?functionName() {private|internal|external|public} [pure|constant|view|payable] [returns()]
在合約中定義函數要以function開頭,后接函數名稱,括號內則是傳入函數中的參數(每一個參數的類型以及名稱要依次寫出,沒有參數則不寫),“returns”后接該函數返回值的類型(無返回值則可以不寫returns)。
⑤用constant、view、pure修飾function分別表示:
? ? constant:只能讀取、不可改變狀態變量(就是contract中定義的變量)
? ? view:只能讀取、不可改變狀態變量,和constant一樣,不消耗燃料
? ? pure:不能讀取也不能改變狀態變量,一個固定的輸入只能有一個固定的輸出
[1]以下情況被認為是修改狀態:修改狀態變量(函數體外部、合約內部創建的變量就是狀態變量)、產生事件、創建其它合約、使用selfdestruct、通過調用發送以太幣、調用任何沒有標記為view或者pure的函數、使用低級調用、使用包含特定操作碼的內聯匯編。
[2]以下被認為是從狀態中進行讀取:讀取狀態變量、訪問this.balance或者 <address>.balance、訪問block,tx,msg中任意成員(除msg.sig和msg.data之外)、調用任何未標記為pure的函數、使用包含某些操作碼的內聯匯編。
⑥用external、public、internal、private修飾function分別表示:
? ? external:外部函數作為合約接口的一部分,意味著可以從其他合約和交易中調用,? 一個外部函數f不能從內部調用(即f不起作用,但this.f()可以,這些后續都會詳細介紹),當收到大量數據的時候,外部函數有時候會更有效率。
? ? public:public函數是合約接口的一部分,可以在內部或通過消息調用。對于public狀態變量,會自動生成一個getter函數。
? ? internal:這些函數和狀態變量只能是內部訪問(即從當前合約內部或從它派生的合約 訪問),不使用this調用。
? ? private:private函數和狀態變量僅在當前定義它們的合約中使用,并且不能被派生合 約使用。
⑦solidity中函數可以遞歸調用。
⑧solidity可以使用import導入其它源文件:
[1]import "filename";??//從“filename”中導入所有的全局符號到當前全局作用域中
[2]import * as symbolName from "filename";??//創建一個新的全局符號symbolName,其成員均來自“filename”中的全局符號
[3]import {symbol1 as alias, symbol2} from "filename";??//創建新的全局符號alias和symbol2,分別從"filename"引用symbol1和symbol2
[4]import "filename" as symbolName;??//這條語句等同于import * as symbolName from "filename";
2、語法——邏輯運算:
//聲明版本號(程序中的版本號要和編譯器版本號一致)
pragma solidity ^0.5.17;
//合約
contract BoolTest
{bool a; ?//創建布爾類型變量時如不初始化,默認賦為falsefunction geta() public returns(bool){return a;}function getnota() public returns(bool){return !a; ?//a當前為false,經過非運算后會返回true}int c = 1;int d = 2;//(這部分與c語言幾乎完全相同,就不列舉所有情形了)function cdequal() public returns(bool){return c==d; ?//c與d當前不相等,c==d不成立,會返回false}function cdequalAnd() public returns(bool){return c==d && true; ?//c==d返回false,false與true進行與運算,結果為false}function cdequalOr() public returns(bool){return c==d || true; ?//c==d返回false,false與true進行或運算,結果為true}function cdnotequalAnd() public returns(bool){return c!=d && true; ?//c與d當前不相等,c!=d成立,返回true,true與true進行或運算,結果為true}
}
3、語法——整型與算術運算:
//聲明版本號(程序中的版本號要和編譯器版本號一致)
pragma solidity ^0.5.17;
//合約
contract MathTest
{//加(a與b為傳入方法中的參數,下同理)function add(uint a,uint b) public returns(uint){return a+b;}//減function minus(uint a,uint b) public returns(uint){return a-b;}//乘function multiply(uint a,uint b) public returns(uint){return a*b;}//除function divide(uint a,uint b) public returns(uint){return a/b;}//取余運算function mod(uint a,uint b) public returns(uint){return a%b;}//冪運算function square(uint a,uint b) public returns(uint){return a**b;}
}
4、語法——位運算:
//聲明版本號(程序中的版本號要和編譯器版本號一致)
pragma solidity ^0.5.17;
//合約
contract MathTest
{//uint8表示大小為8個位的無符號整型uint8 a = 3; ?//二進制表示為00000011uint8 b = 4; ?//二進制表示為00000100//按位與(該部分和c語言也幾乎完全相同)function bitwiseAnd() public returns(uint8){return a&b;}//按位或function bitwiseOr() public returns(uint8){return a|b;}//按位取反function tilde() public returns(uint8){return ~a;}//按位異或function caret() public returns(uint8){return a^b;}//左移(左移運算符后接左移的位數)function leftShift() public returns(uint8){return a<<1;}//右移(右移運算符后接右移的位數)function rightShift() public returns(uint8){return a>>1;}
}
(圖源:https://blog.csdn.net/aiwaston/article/details/113665723)
5、語法——復合運算:
//聲明版本號(程序中的版本號要和編譯器版本號一致)
pragma solidity ^0.5.17;
//合約
contract MathTest
{uint a = 1;//該語法也和c語言基本完全相同//輸出afunction add1() public returns(uint){return a++; ?//執行完該語句后a的值會加1}//輸出a+1function add2() public returns(uint){return ++a; ?//執行該語句前a的值會加1,然后再執行該語句}//輸出afunction sub1() public returns(uint){return a--; ?//執行完該語句后a的值會減1}//輸出a-1function sub2() public returns(uint){return --a; ?//執行該語句前a的值會減1,然后再執行該語句}
}
6、現象——整型溢出:
//聲明版本號(程序中的版本號要和編譯器版本號一致)
pragma solidity ^0.5.17;
//合約
contract MathTest
{//這部分也與c語言幾乎完全相同function flow() view public returns(uint8){uint8 mm ?= 255; ?//二進制為11111111mm++;return mm; ?//mm只有8位,但是對于mm,再加1會得到100000000,只能取前8位,于是返回0而不是256}function flow2() view public returns(uint256){uint8 mm ?= 255;mm++;return mm; ?//即使返回值聲明有256位,但是在“mm++”這一步mm就被修改為0,所以返回的還是0}function flow3() view public returns(uint){uint mm ?= 255; ?//這次沒對mm的位數進行限制,它可以裝下100000000mm++;return mm;}function flow4() view public returns(uint8){uint8 nn = 0;nn--;return nn; ?//對00000000做減法,會先給“第九位”添1(要明確nn的位數),那么就會得到結果11111111,轉換成十進制就是255(對于有符號數,就涉及到了補碼的運算,同理,先轉換成補碼按此規律運算,再轉換回十進制)}
}
7、現象——非法運算:
//聲明版本號(程序中的版本號要和編譯器版本號一致)
pragma solidity ^0.5.17;
//合約
contract MathTest
{function errorTest() view public returns(int){int a = 2;int b = 3;return a/b; ?//既然聲明了返回值為整型,那么就不可能得到小數//不過solidity允許整型運算的過程中存在小數,只是結果不能接納小數,比如2/5-2/5//solidity會將表達式計算出結果后再按照一定的規則(比如整形溢出等)進行處理,比如2**99-2**99+1}function errorTest2() view public returns(int){int a = 2;int b = 0;return a/b; ?//0不能做除數,這個方法甚至不能成功編譯}function errorTest3() view public returns(int){int a = -1;int b = 0;return b >> a; ?//位移運算符不接受負數}}
8、語法——固定長度字節數組:
//聲明版本號(程序中的版本號要和編譯器版本號一致)
pragma solidity ^0.5.17;
//合約
contract ByteArray
{bytes1 public num1 = 0x7a; ?//1個字節長度的變量bytes2 public num2 = 0x7a68; ?//2個字節長度的變量bytes12 public num3 = 0x7a68656e676a69616e78756e; ?//12個字節長度的變量
}
(1)在solidity中,直接用public聲明成員變量,編譯部署后,會生成一個默認的get方法,通過該方法可以直接查看這個成員屬性,而不需要通過自擬函數返回這個成員屬性。
(2)字節數組同樣支持位運算以及邏輯運算。
(3)固定長度字節數組不可以僅僅對某個字段進行更改(可以僅對某個字段進行讀取,如下表所示),要么就直接對其整體重新賦值。
名稱 | num3[0] | num3[1] | num3[2] | num3[3] | num3[4] | num3[5] | num3[6] | num3[7] | num3[8] | num3[9] | num3[10] | num3[11] |
內容 | 0x7a | 0x68 | 0x65 | 0x6e | 0x67 | 0x6a | 0x69 | 0x61 | 0x6e | 0x78 | 0x75 | 0x6e |
(4)定長字節數組的關鍵字有bytes1,bytes2,bytes3,...,bytes32。
(5)不管是定長數組還是動態長度數組,都不能越界訪問,否則會報錯。
9、語法——動態長度字節數組:
//聲明版本號(程序中的版本號要和編譯器版本號一致)
pragma solidity ^0.5.17;
//合約
contract DynamicByteArray
{bytes public name = new bytes(2); ?//為動態字節數組開辟2個字節的空間function initName() public{name[0] = 0x7a; //對動態長度字節數組進行初始化name[1] = 0x68;}function getLength() view public returns(uint){return name.length; ?//8個位(一個字節)為一個長度單位}function changeName() public{name[0] = 0x88; ?//可以對某一字段進行修改}function changeLength() public{name.length = 5; ?//可以修改數組的長度,不過修改的同時會將數組的內容全部置為0}function pushTest() public{name.push(0x99); ?//可以在數組末尾追加字節元素,同時增加數組的長度}
}
(1)動態長度字節數組可以僅對某個字段進行更改。
(2)可以通過修改動態長度數組的length屬性修改動態長度數組的長度,不過修改的同時會將數組的內容全部置為0。
(3)動態長度數組有push方法,使用該方法可以在數組末尾追加指定的字節元素,同時增加數組的長度。