Solidity从入门到精通-Remix的基本使用和Solidity的基本数据类型
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编程的入门指引,帮助读者快速掌握编写智能合约的基本技能。