Solidity從入門到精通-Remix的基本使用和Solidity的基本數據類型
講了那么多理論,相信大家對區塊鏈/web3也有了一定認知;這時候可能就問有人會問了如何把理論變成實際的代碼實現。
這就來了接下來會給大家分享Solidity入門教程
這時候就會有同學問了Solidity是什么?
Solidity 是一種用于編寫以太坊虛擬機(EVM)智能合約的編程語言;掌握Solidity是參與鏈上項目的必備技能。
Solidity 兩大特點 :基于對象,高級
- “基于對象”:學會Solidity后,可以輕松的找到一些區塊鏈領域的好工作
- “高級”:不會Solodity,會很low
相信大家接觸過編程的第一行代碼都是"Hello World",無論是學C語言的,還是學Java的,還是學Python的,那我們今天也就從"Hello World"開始本次的Solidity。
一、Solidity編程工具介紹-Remix
相信大家寫代碼都是使用編程應用來的,當然了有一些大佬用notepad++,記事本;本人是菜鳥就用編程應用來寫了。接下來我介紹一下Solidity的編程工具-Remix。
這時候就會有同學問了為什么要選擇Remix呢?
- Remix是以太坊官方推薦的智能合約集成開發環境(IDE)
- 適合新手,可以快速開發和部署合約,無需在本地安裝任何程序
我們使用Java,C,Python都要本地安裝環境,對新手非常不友好,但是Remix自己集成了環境直接上手。
Remix 網址:https://remix.ethereum.org
可以直接網頁打開
Remix的使用上手
在Remix中,左側菜單欄有六個按鈕,分別對應文件夾(存放編寫的代碼文件)、搜索工具、編輯(允許代碼)、部署(將合約部署到鏈上)、Dubug(斷點查看程序運行bug)和Git代碼分支。
二、第一個Solidity程序
我們新建一個文件給他命名“helloWeb3.sol”;新建完成后他就是一個簡單的空白文件。
外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳
接下來寫代碼
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
contract HelloWeb3{string public _string = "Hello Web3!";
}
代碼分析
第一行代碼是注釋,說明代碼中所使用的軟件許可(license),這里使用的是MIT許可。如果不寫許可會有警告出現(warning),但是不影響程序的運行。Solidity 注釋 以“//”開頭,后面跟上注釋的內容,注釋的代碼不會被EVM執行
// SPDX-License-Identifier: MIT
第二行代碼聲明源文件所使用的Solidity的版本,因為不同的版本的語法有差異。這行代碼表示源文件將不允許小于0.8.21或大于等于0.9.0的編譯。(第二個條件由 ^
提供)。Solidity 語句以分號(;)結尾。
pragma solidity ^0.8.21;
第三四行代碼。創建合約使用(contract),并聲明合約名為 “HelloWeb3”。然后聲明一個string(字符串)變量,復制為“Hello Web3”。
contract HelloWeb3 {string public _string = "Hello Web3!";
}
編譯部署代碼
代碼寫完之后 ctrl+s保存代碼Remix就會自動編譯;然后編譯模塊會有綠色的?出現,編譯成功
編譯成功后我們進入部署界面;選擇對應的合約,然后點擊部署
我們可以看到下方有一個“已部署和合約”,看到_string的參數值為“Hello Web3!”
三、Solidity的數據類型
Solidity中的數據類型有三大類型
- 值類型(Value Type):包括布爾型,整數型,地址類型等等;這類變量賦值時候直接傳遞數值。
- 引用類型(Reference Type):包括數組和結構體,這類變量占空間大,賦值時候直接傳遞地址(類似于Java的地址值,指針)
- 映射類型(Mapping Type):Solidity中存儲健值對的數據結構,可以理解為哈希表。(和Java中的map一個用法)
我們本次就介紹常用的類型,一些不常用的就不介紹了,大家遇到了可以去查閱資料。
一、值類型
1.布爾型
布爾型是兩值變量,值為“true”或“false”
bool public _bool = true;
布爾值的運算符包括:
!
(邏輯非)&&
(邏輯與,“and”)||
(邏輯或,“or”)==
(等于)!=
(不等于)
bool public _bool1 = !_bool; //取非bool public _bool2 = _bool && _bool1; //與bool public _bool3 = _bool || _bool1; //或bool public _bool4 = _bool == _bool1; //相等bool public _bool5 = _bool != _bool1; //不等
這一塊和所有的編程語言都是一樣的;我們可以看到編譯后的結果數據
2.整型
整型是Solidity中的整數(Java中的int類型),最常用的
// 整型int public _int = -1; //整數,包括負數uint public _uint = 1; //無符號uint256 public _number = 20220330;
常用的整型運算符包括:
- 比較運算符(返回布爾值):
<=
,<
,==
,!=
,>=
,>
- 算術運算符:
+
,-
,*
,/
,%
(取余),**
(冪)
//整數運算uint256 public _number1 = _number + 1;uint256 public _number2 = 2**2;uint256 public _number3 = 7%2;bool public _numberbool = _number2 > _number3;
3.地址型
地址類型(address)有兩類:
- 普通地址(address): 存儲一個 20 字節的值(以太坊地址的大小)。
- payable address: 比普通地址多了
transfer
和send
兩個成員方法,用于接收轉賬。
// 地址address public _address = 0x7A58c0Be72BE218B41C608b7Fe7C5bB630736C71;address payable public _address1 = payable(_address); // payable address 可以用于轉賬、查余額// 地址類型的成員uint256 public balance = _address1.balance; //balance of address
4.長字節數組
字節數組分為定長和不定長兩種:
- 定長字節數組: 屬于值類型,數組長度在聲明之后不能改變。根據字節數組的長度分為
bytes1
,bytes8
,bytes32
等類型。定長字節數組最多存儲 32 bytes 數據,即bytes32
。 - 不定長字節數組: 屬于引用類型(之后的章節介紹),數組長度在聲明之后可以改變,包括
bytes
等。
// 固定長度的字節數組bytes32 public _btye32 = "MiniSolidity";bytes1 public _byte = _btye32[0];
在上述代碼中,字符串 MiniSolidity
以字節的方式存儲進變量 _byte32
。如果把它轉換成 16 進制
,就是:0x4d696e69536f6c69646974790000000000000000000000000000000000000000
_byte
變量的值為 _byte32
的第一個字節,即 0x4d
。
5.枚舉
枚舉(enum
)是 Solidity 中用戶定義的數據類型。它主要用于為 uint
分配名稱,使程序易于閱讀和維護。它與 C 語言
中的 enum
類似,使用名稱來代替從 0
開始的 uint
:
// 用enum將uint 0, 1, 2表示為Buy, Hold, Sell
enum ActionSet { Buy, Hold, Sell }
// 創建enum變量 action
ActionSet action = ActionSet.Buy;
復制代碼
枚舉可以顯式地和 uint
相互轉換,并會檢查轉換的無符號整數是否在枚舉的長度內,否則會報錯:
// enum可以和uint顯式的轉換
function enumToUint() external view returns(uint){return uint(action);
}
enum
是一個比較冷門的數據類型,幾乎沒什么人用。
二、引用類型
我們來講講一講在Solidity中兩個重要的引用類型:數組array和結構體struct
數組array
數組(Array
)是Solidity
常用的一種變量類型,用來存儲一組數據(整數,字節,地址等等)。數組分為固定長度數組和可變長度數組兩種:
- 固定長度數組:在聲明時指定數組的長度。用
T[k]
的格式聲明,其中T
是元素的類型,k
是長度,例如:
// 固定長度 Array
uint[8] array1;
bytes1[5] array2;
address[100] array3;
- 可變長度數組(動態數組):在聲明時不指定數組的長度。用
T[]
的格式聲明,其中T
是元素的類型,例如:
// 可變長度 Array
uint[] array4;
bytes1[] array5;
address[] array6;
bytes array7;
注意:bytes
比較特殊,是數組,但是不用加[]
。另外,不能用byte[]
聲明單字節數組,可以使用bytes
或bytes1[]
。bytes
比 bytes1[]
省gas。
創建數組的規則
在Solidity里,創建數組有一些規則:
- 對于
memory
修飾的動態數組
,可以用new
操作符來創建,但是必須聲明長度,并且聲明后長度不能改變。例子:
```solidity
// memory動態數組
uint[] memory array8 = new uint[](5);
bytes memory array9 = new bytes(9);
```
- 數組字面常數(Array Literals)是寫作表達式形式的數組,用方括號包著來初始化array的一種方式,并且里面每一個元素的type是以第一個元素為準的,例如
[1,2,3]
里面所有的元素都是uint8
類型,因為在Solidity中,如果一個值沒有指定type的話,會根據上下文推斷出元素的類型,默認就是最小單位的type,這里默認最小單位類型是uint8
。而[uint(1),2,3]
里面的元素都是uint
類型,因為第一個元素指定了是uint
類型了,里面每一個元素的type都以第一個元素為準。
下面的例子中,如果沒有對傳入 `g()` 函數的數組進行 `uint` 轉換,是會報錯的。```solidity
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.4.16 <0.9.0;contract C {function f() public pure {g([uint(1), 2, 3]);}function g(uint[3] memory _data) public pure {// ...}
}
```
- 如果創建的是動態數組,你需要一個一個元素的賦值。
```solidity
uint[] memory x = new uint[](3);
x[0] = 1;
x[1] = 3;
x[2] = 4;
```
數組成員
length
: 數組有一個包含元素數量的length
成員,memory
數組的長度在創建后是固定的。push()
:動態數組
擁有push()
成員,可以在數組最后添加一個0
元素,并返回該元素的引用。push(x)
:動態數組
擁有push(x)
成員,可以在數組最后添加一個x
元素。pop()
:動態數組
擁有pop()
成員,可以移除數組最后一個元素。
結構體struct
Solidity
支持通過構造結構體的形式定義新的類型(有點類似于Java中的對象)。結構體中的元素可以是原始類型,也可以是引用類型;結構體可以作為數組或映射的元素。創建結構體的方法:
// 結構體
struct Student{uint256 id;uint256 score;
}Student student; // 初始一個student結構體
給結構體賦值的四種方法:
// 給結構體賦值
// 方法1:在函數中創建一個storage的struct引用
function initStudent1() external{Student storage _student = student; // assign a copy of student_student.id = 11;_student.score = 100;
}
結構體我們需要通過Debug的方式來看到對應的數據信息;如下圖,這樣我們就可以清楚的看到結構體里面的數據信息了。
三、映射類型
接下來我們來介紹一下映射類型Mapping,Solidity中存儲鍵值對的數據結構,可以理解為哈希表。
在映射中,人們可以通過鍵(Key
)來查詢對應的值(Value
),比如:通過一個人的id
來查詢他的錢包地址。
聲明映射的格式為mapping(_KeyType => _ValueType)
,其中_KeyType
和_ValueType
分別是Key
和Value
的變量類型。例子:
mapping(uint => address) public idToAddress; // id映射到地址
mapping(address => address) public swapPair; // 幣對的映射,地址到地址
映射的規則
- 規則1**:映射的
_KeyType
只能選擇Solidity內置的值類型,比如uint
,address
等,不能用自定義的結構體。而_ValueType
可以使用自定義的類型。下面這個例子會報錯,因為_KeyType
使用了我們自定義的結構體:
// 我們定義一個結構體 Struct
struct Student{uint256 id;uint256 score;
}
mapping(Student => uint) public testVar;
-
規則2:映射的存儲位置必須是
storage
,因此可以用于合約的狀態變量,函數中的storage
變量和library函數的參數。不能用于public
函數的參數或返回結果中,因為mapping
記錄的是一種關系 (key - value pair)。 -
規則3:如果映射聲明為
public
,那么Solidity會自動給你創建一個getter
函數,可以通過Key
來查詢對應的Value
。 -
規則4*:給映射新增的鍵值對的語法為
_Var[_Key] = _Value
,其中_Var
是映射變量名,_Key
和_Value
對應新增的鍵值對。例子:
function writeMap (uint _Key, address _Value) public{idToAddress[_Key] = _Value;
}
映射原理
- 原理1: 映射不儲存任何鍵(
Key
)的資訊,也沒有length的資訊。 - 原理2*: 對于映射使用
keccak256(h(key) . slot)
計算存取value的位置。感興趣的可以去閱讀 WTF Solidity 內部規則: 映射存儲布局 - 原理3: 因為Ethereum會定義所有未使用的空間為0,所以未賦值(
Value
)的鍵(Key
)初始值都是各個type的默認值,如uint的默認值是0。
總結
本文介紹了Solidity編程語言的基礎知識和開發工具Remix的使用。首先講解了Solidity的特點及其在區塊鏈開發中的重要性,然后詳細介紹了Remix IDE的使用方法,包括如何編寫、編譯和部署第一個智能合約"HelloWeb3"。接著重點講解了Solidity的三種數據類型:值類型(布爾型、整型、地址型、長字節數組和枚舉)、引用類型和映射類型,通過代碼示例展示了各種類型的具體用法和運算操作。文章為初學者提供了Solidity編程的入門指引,幫助讀者快速掌握編寫智能合約的基本技能。