提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 注释
- 变量
- 数据类型
- 布尔类型与运算符
- 整型
- 运算符
- 地址类型
- 静态字节数组
- 字面值
- 枚举类型
- 自定义值类型
- 数组
- 结构体
- 映射类型(mapping)
-
- [如何使用 key 存取 value](#如何使用 key 存取 value)
- [映射类型只能声明在 storage](#映射类型只能声明在 storage)
- 映射类型可以与数组,结构体互相嵌套
注释
当你编写Solidity合约时,添加注释对解释代码的主要功能或使用场景是非常重要的。注释仅供开发者阅读,不会对程序的执行产生任何影响。在Solidity中,你可以通过两种方式来添加注释:
- 使用 // 来添加单行注释。
- 使用 /.../ 来添加多行注释。
bash
// This is a single-line comment.
/*
This is a
multi-line comment.
*/
变量
bash
uint x = 3;
uint y = 4;

数据位置
我们之前提到,变量是存储在某种介质上的。在传统应用程序中,数据可能存储在内存或硬盘上。存储在内存中的数据是易失的,程序一旦退出运行,这些数据就会消失。而存储在硬盘上的数据是持久的,当程序再次运行时,这些数据会被重新读取。同样地,在智能合约中,也存在不同的数据存储位置,这些位置决定了数据的持久性。
Solidity 中有三种数据存储位置,分别指定变量的存储方式:
- storage: 数据永久存储在区块链上,通常用于状态变量。
- memory: 数据存储在内存中,是临时的,当函数调用结束后数据就会消失。
- calldata: 数据存储在一个专门用于存放函数参数的区域,这也是临时的。
在这些存储位置中,标记为 storage 的数据位置通常关联到我们所说的"状态变量",它们的数据是永久记录在区块链上的。这种机制确保了区块链数据的不可变性和合约状态的持续性。
声明与定义
在很多资料中,你可能会看到"声明"(declaration)和"定义"(definition)这两个概念的区分。一般来说,声明是指为变量指定名字和数据类型的过程,而定义则不仅包括指定名称和数据类型,还包括为变量分配存储空间和赋予初始值。
然而,在我们接下来的讨论中,我们不会特别区分这两个概念。我们会交替使用"声明"和"定义",主要是因为在编写智能合约的上下文中,这种区分并不会对我们的工作产生重大影响。过于严格地区分这两者可能反而会使文章变得更难理解。
在 Solidity 中,一旦变量被声明,如果没有手动初始化,它会自动被赋予一个默认的初始值,这通常称为"零值初始化"。因此,不论变量是否被显式初始化,它最终都会拥有一个初始值。这就是为什么在 Solidity 的环境下,区分声明与定义不是特别必要的原因。
数据类型
智能合约在本质上是计算机程序,因此它能够处理多种不同的数据类型,每种类型都有其特定的表示方式和操作方法。在 Solidity 中,基于参数传递的方式,数据类型可以分为两大类:「值类型」和「引用类型」。
- 值类型:这类类型的数据在传递时会被复制,每次传递都是一个独立的副本。
- 引用类型:相对于值类型,引用类型的数据在传递时不复制其本身,而是传递对原始数据的引用。
值传递
bash
uint8 a = 1;
uint8 b = a;
引用传递
传递数据地址的方法通常被称为「引用传递」(pass by reference)。在这种方法中,赋值或参数传递时传递的是数据的地址,而不是数据本身。
在下面的示例中,我们定义了两个字节数组 bts1 和 bts2。在代码的第二行,通过赋值 bts2 = bts1,bts2 和 bts1 都开始指向同一个数据地址。因此,当修改其中任何一个数组的内容时,另一个数组的内容也会相应地发生变化,因为它们共享同一个存储位置。这展示了引用传递如何在实际应用中工作,以及它如何影响关联变量的值。
bash
bytes memory bts1 = "btc";
bytes memory bts2 = bts1;
console.log("bts1: %s", string(bts1)); // bts1: btc
console.log("bts2: %s", string(bts2)); // bts2: btc
bts2[0] = 'e'; //这里只改了bts2[0]的值,但是你会发现bts1[0]的值也会跟着变动
console.log("bts1: %s", string(bts1)); // bts1: etc
console.log("bts2: %s", string(bts2)); // bts2: etc
布尔类型与运算符
布尔类型是只有 true 或 false 两种可能取值的类型。在 Solidity 中,布尔类型变量可以使用 bool 关键字进行定义。
运算符
布尔类型可以使用的运算符和作用如下所示,运算操作后得到的结果依然是布尔类型。
- ! (逻辑非)
- && (逻辑与)
- || (逻辑或 )
- == (等于)
- != (不等于)
短路规则
需要注意的是,逻辑运算符 || 和 && 在 Solidity 中都遵循短路规则(short-circuiting)。其规则如下:
- 对于 f(x) || g(y),如果 f(x) 为 true,则 g(y) 不会被执行。
- 对于 f(x) && g(y),如果 f(x) 为 false,则 g(y) 不会被执行。
&&和 || 运算子的短路规则
以下是一个示例,包含两个函数 isEven 和 isZero。其中,isEven 判断一个数是否为偶数,isZero 判断一个数是否为 0。如果传入的参数为 0,isZero 会产生副作用,使得 zeroCount 加 1。
bash
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.17;
contract BoolShortCircuit {
uint256 public zeroCount = 0;
function isEven(uint256 num) private pure returns(bool) {
return num%2 == 0;
}
// isZero函数有副作用,会改变状态变量zeroCount的值
function isZero(uint256 num) private returns(bool) {
if(num == 0) {
zeroCount += 1; // 函数副作用,会改变zeroCount的值
}
return num == 0;
}
}
整型
整型(integer)是不包含小数部分的数值型数据,包括正整数、负整数和 0 等。账户余额、Gas、持有的 Token 数量等通常都用整型表示。在 Solidity 中,整型有两种类型:
- intM:有符号整型
- uintM:无符号整型
其中,M 的取值范围为 8 到 256,步长为 8。例如,有 int8、int16、int32 等等,一直到 int256。相应地,也有 uint8、uint16、uint32,一直到 uint256。其中,int8 和 uint8 占用 8 位(8 bits),int16 和 uint16 占用 16 位,以此类推。
运算符
算术运算符
算术运算符可以用来进行四则运算,得到的结果是整型。
- +(加)
- -(减)
- *(乘)
- /(除)
- %(取模)
- **(幂)
- <<(左移)
- >>(右移)
比较运算符
通过比较运算符,我们可以比较两个变量的数量大小关系,以及变量是否相等。比较运算符得到的结果是布尔值。
bash
- <=(小于等于)
- < (小于)
- ==(等于)
- !=(不等于)
- >=(大于等于)
- > (大于)
位运算符
bash
位运算符用来对二进制位进行操作,其执行结果是整型。
&(按位与)
|(按位或)
^(按位异或)
~(按位取反)
地址类型
地址类型(address)是 Solidity 独有的一种类型,用于存放账户地址。在给其他账户转账或与其他合约交互时,需要使用地址类型。类似于向他人银行账户转账时需要知道对方的账户号码,Solidity 的地址类型也扮演着类似的角色。
Solidity 的地址类型用关键字 address 表示。它占据 20 字节(160 位),默认值为 0x0,表示空地址。地址类型可以细分为两种:
- address:普通地址类型(不可接收转账)
- address payable:可收款地址类型(可接收转账)
这两种地址类型的主要区别在于,address payable 能接受转账,而 address 不能。接下来,我们将先介绍如何定义地址类型的变量,然后再解释为什么要区分这两种地址类型。
bash
address addr = 0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990;
address payable addr_pay = payable(0x8306300ffd616049FD7e4b0354a64Da835c1A81C);
到现在为止,我们已经了解了地址类型的基本作用是存放账户地址。但是,我们仍有一个疑问:address 和 address payable 看起来区别不大,为什么要区分它们?直接统一使用 address 类型,想要转账就转账,不想转账就不转不就可以了吗?
Solidity 之所以要进行这样的区分,是为了提高合约的安全性,避免 Ether 转入某些合约后无法转出,导致资金永远被锁住。
首先,我们需要了解 Solidity 中账户有两种类型:外部账户(externally owned address,简称 EOA)和合约账户(contract address,简称 CA)。EOA 是我们在 MetaMask 上创建的那些账户,而 CA 是在部署合约后生成的合约地址。
当我们将 Ether 转入 EOA 后,只要我们控制了 EOA 的私钥,就可以将 Ether 再转出来。然而,CA 账户情况则不同。CA 账户是由合约控制的,合约只能执行其定义过的操作。因此,我们必须在合约中实现一个函数,定义如何将账户中的 Ether 转出,否则这些 Ether 会被永远锁在 CA 账户中。
因此,每次向 CA 账户转账时,我们都必须问自己:这个合约是否已经定义了转出 Ether 的逻辑。使用 address payable 明确告诉编译器你已确认转账到该地址是安全的。这不仅提高了合约的安全性,也更方便开发者进行调试。
address 和 address payable 之间可以互相进行类型转换。主要遵循两条规则。
- address payable 可以隐式地被转换成 address
- address 需要显式地使用 payable(addr) 函数转换成 address payable
bash
address payable addr_pay = payable(0x8306300ffd616049FD7e4b0354a64Da835c1A81C);
address addr = addr_pay; _// 隐式类型转换_
address addr = 0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990;
address payable addr_pay = payable(addr); _// 显式类型转换_
成员变量
地址类型有三个成员变量,分别为:
- balance :该地址的账户余额,单位是 Wei
- code :该地址的合约代码,EOA 账户为空,CA 账户为非空
- codehash :该地址的合约代码的 hash 值
获取成员变量值
下面展示了如何获取地址的成员变量。其中 this 代表的是当前合约。
bash
function get_balance() public view returns(uint256) {
return address(this).balance; _//获取地址账户余额_
}
function get_code() public view returns(bytes memory) {
return address(this).code; _//获取合约代码_
}
function get_codehash() public view returns(bytes32) {
return address(this).codehash; _//获取合约代码的hash值_
}
成员函数
地址类型有五个成员函数:
- transfer(uint256 amount):向指定地址转账,失败时抛出异常(仅 address payable 可以使用)。
- send(uint256 amount):与 transfer 函数类似,但失败时不会抛出异常,而是返回布尔值(仅 address payable 可以使用)。
- call(...):调用其他合约中的函数。
- delegatecall(...):与 call 类似,但使用当前合约的上下文来调用其他合约中的函数,修改的是当前合约的数据存储。
msg.sender 和 msg.value
在 Solidity 中,msg.sender 和 msg.value 是全局上下文变量(属于 msg 命名空间),用于获取当前交易 / 调用的核心信息,是智能合约开发中最常用的变量之一。以下是详细解析:
- 一、核心概念:msg 命名空间
msg 是 Solidity 内置的全局对象,包含当前调用(交易 / 内部调用)的上下文信息,所有 msg.* 变量的取值在单次函数执行中是固定的(即使内部调用其他函数,也不会改变)。 - 二、msg.sender:调用者 / 发起者地址
- 定义
msg.sender 返回当前函数调用的直接发起者的以太坊地址(类型为 address/address payable)。 - 关键特性
类型:Solidity 0.8.x 中默认是 address,如需接收 ETH 需显式转换为 address payable(如 payable(msg.sender))。
调用层级:
外部用户直接调用合约:msg.sender = 用户钱包地址;
合约 A 调用合约 B 的函数:msg.sender = 合约 A 的地址(而非最终用户地址);
内部函数调用(同一合约内):msg.sender 仍为外部调用的发起者(不会变为合约自身地址)。
不可篡改:由以太坊虚拟机(EVM)填充,合约无法主动修改,是身份验证的核心依据。 - 典型用途
权限控制(如仅合约所有者可调用函数);
记录资产归属(如 NFT 铸造、代币转账的发起者);
验证操作发起者身份。
示例代码
bash
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract SenderExample {
address public owner;
mapping(address => uint) public userBalances;
constructor() {
// 部署合约的地址成为所有者
owner = msg.sender;
}
// 仅所有者可调用的函数
function onlyOwner() public view {
require(msg.sender == owner, "Not owner");
}
// 记录调用者的余额
function deposit() public payable {
// msg.sender 是调用 deposit 的地址
userBalances[msg.sender] += msg.value;
}
}
- 三、msg.value:附带的 ETH 金额
- 定义
msg.value 返回当前调用中附带的 ETH 数量(单位为 wei,1 ETH = 10^18 wei),类型为 uint256。 - 关键特性
仅适用于 payable 函数:如果非 payable 函数被调用时附带 ETH,交易会直接回滚;
调用层级:
外部用户调用合约并转 ETH:msg.value = 用户发送的 ETH 数量;
合约 A 调用合约 B 的 payable 函数并转 ETH:msg.value = 合约 A 转发的 ETH 数量;
无 ETH 附带时:msg.value = 0;
单位:默认是 wei,需通过单位换算(如 ether、gwei)提升可读性。 - 典型用途
接收 ETH 并验证金额(如众筹、NFT 铸造定价);
转账时附带 ETH(结合 payable 地址);
计算用户充值的 ETH 数量。
示例代码
bash
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract ValueExample {
// NFT 铸造价格:0.1 ETH
uint256 public mintPrice = 0.1 ether;
function mintNFT() public payable {
// 验证发送的 ETH 等于铸造价格
require(msg.value == mintPrice, "Invalid ETH amount");
// 可执行铸造逻辑...
// 向调用者退款(如有多余)
if (msg.value > mintPrice) {
// 需将 msg.sender 转为 payable 才能转账
payable(msg.sender).transfer(msg.value - mintPrice);
}
}
// 查看当前合约余额
function getContractBalance() public view returns (uint256) {
return address(this).balance;
}
}
静态字节数组
静态字节数组(fixed-size byte arrays)是 Solidity 中独有的一种数据类型。与其字面意义相符,它们具有固定长度的字节数组。基于静态字节数组,你可以构建更为紧凑、节省存储空间的数据类型。
Solidity 共有 32 种静态字节数组:bytes1、bytes2、bytes3,依此类推,直至 bytes32。更好地理解这一概念是将这 32 种静态字节数组视为 32 种不同的值类型,而非通常意义上的数组。这 32 种静态字节数组类似于大小各异的乐高积木,通过将它们嵌入到"结构体"中并进行排列组合,我们可以构建出新的数据类型。
下标访问
bash
bytes3 s = 'abc';
bytes1 ch = s[1]; _// ch的值为0x62,也就是'b'的ascii值_
字面值
字面值(literal)是指在程序中无需变量保存,直接表示为具体数字或字符串值的方式。Solidity 支持多种字面值类型,包括:
- 地址字面值
- 有理数和整数字面值
- 字符串字面值
- Unicode 字面值
枚举类型
枚举是一种组织和收集有关联变量的方法。我们逐一列举变量可能的取值,将它们收集在一起,并为它们取一个统一的名称,这样就定义了一个枚举类型。举例来说,当你开发一个链游时,也许需要根据键盘输入的上下左右来控制游戏角色的动作。这时,你可以定义一个名为 ActionChoices 的枚举类型,其中包含上下左右四个有限的取值。
Solidity 的枚举类型与 C 语言中的相似,都是一种特殊的整型。它定义了一组名称和整数值之间的对应关系,内部表示为从 0 开始的正整数。
bash
enum ActionChoices {
GoLeft, _// 底层表示为 0 _
GoRight, _// 底层表示为 1_
GoUp, _// 底层表示为 2_
GoDown _// 底层表示为 3_
}
使用枚举值
bash
ActionChoices choice = ActionChoices.GoLeft;
自定义值类型
"自定义值类型"(User Defined Value Types)是用户定义的一种"值类型"。与之类似的另一种自定义类型是结构体,不过结构体是引用类型,我们稍后会详细介绍。"自定义值类型"类似于别名,但并不等同于别名。别名与原类型完全相同,只是名称不同。然而,"自定义值类型"则不具备原始类型的操作符(如 +、-、*、/等),其主要价值在于提高类型安全性和代码可读性。
「自定义值类型」的定义 通过 type C is V 可以定义新的"自定义值类型",其中 C 是新定义的类型,V 必须是 Solidity 的原生类型。例如,下面的例子定义了两种新类型:
bash
type Weight is uint128;
type Price is uint128;
数组
数组的声明
数组是一种数据结构,用于存储同类型元素的有序集合。根据数组长度是否可以改变,可以将数组分为以下两种类型:
- 静态数组(Static Array)
- 动态数组(Dynamic Array)
静态数组声明
bash
T[arrSize] DataLocation arrName;
其中,arrSize 表示数组的长度,DataLocation 表示数据位置,而 arrName 则是你为这个数组取的任意名字。
数组是一种引用类型。请注意:在声明和定义数组时,必须使用三个数据位置关键字之一:storage、memory、calldata。
声明静态数组
bash
uint[3] memory nftMem;
uint[3] storage nftStorage;
在 Solidity 中,需要注意静态数组的大小必须在编译时确定。这意味着你不能使用变量来指定数组的大小。举例来说,下面的代码是不合法的:
bash
uint size = 2;
uint[size][size] memory array; _// 非法,size 是变量,不能用来指定数组大小_
动态数组声明
假设 T 是一种类型,那么动态数组的声明格式如下:
bash
T[] DataLocation arrName;
bash
uint[] memory nftMem;
uint[] storage nftStorage;
数组的初始化
静态数组的初始化
- 零值初始化
bash
uint[3] memory nftArr; _//所有元素都是0_
- 数组字面值初始化
bash
_//必须使用uint(1000)显式地将「数组字面值」第一个元素的类型转换成uint_
uint[3] memory nftArr = [uint(1000), 1001, 1002];
需要注意的是,数组字面值的基础类型是由其第一个元素的类型确定的。例如,在上面的例子中,[uint(1000), 1001, 1002] 的基础类型是 uint,因为第一个元素是 uint(1000)。其他所有元素都会隐式地被转换成第一个元素的类型。
动态数组初始化
动态数组的初始化需要使用关键字 new。它的所有元素值会被「零值初始化」,即赋予默认值。以下是一个整型动态数组的初始化示例:
初始化了一个有三个元素的动态数组,元素值被初始化为零值
bash
uint n = 3;
uint[] memory nftArr = new uint[](n);
bash
uint[] storageArr = [uint(1), 2]; _// 动态数组只有在storage位置才能用数组字面值初始化_
静态数组和动态数组是不同的类型,因此它们之间不能相互赋值
下标访问
bash
uint[3] memory nftArr1 = [uint(1000), 1001, 1002];
nftArr1[0] = 2000;
nftArr1[1] = 2001;
nftArr1[2] = 2002;
uint[] memory nftArr2 = new uint[](3);
nftArr2[0] = 1000;
nftArr2[1] = 1001;
nftArr2[2] = 1002;
数组切片
数组切片(array slice)是建立在数组基础上的一种视图(view)。其语法形式为 arr[start:end]。这个视图包含的是从索引 start 到索引 end-1 的元素。与数组不同的是,切片是没有具体类型的,也不会占据存储空间。它是一种方便我们处理数据的抽象方式。
数组切片只能作用于 calldata
bash
uint[5] storageArr = [uint(0), 1, 2, 3, 4];
function foo() public {
uint[3] storage s1 = storageArr[1:4]; _// 编译错误,不能对 storage 位置的数组进行切片_
uint[5] memory memArr = [uint(0), 1, 2, 3, 4];
uint[3] memory s2 = memArr[1:4]; _// 编译错误,不能对 memory 位置的数组进行切片_
}
数组成员
数组是一种组合数据类型,它包含若干成员变量和成员函数。在本节中,我们将详细介绍这些成员变量和成员函数。
成员变量 数组,无论是静态还是动态,都具有一个成员变量:length。这个成员变量记录了数组的长度。我们可以通过点操作符(.)来访问这个值:
获取数组的长度
bash
uint[3] memory arr1 = [uint(1000), 1001, 1002];
uint[] memory arr2 = new uint[](3);
uint arr1Len = arr1.length; _// 3_
uint arr2Len = arr2.length; _// 3_
数组成员函数
只有当动态数组位于存储(storage)位置时,它才具备成员函数。与此相对,静态数组以及位于 calldata 或 memory 中的动态数组不具备任何成员函数。这些成员函数可以改变数组的长度,具体包括:
- push():在数组末尾增加一个元素,并赋予零值,使得数组长度增加一。
- push(x):将元素 x 添加到数组末尾,同样使数组长度增加一。
- pop():从数组末尾移除一个元素,导致数组长度减少一。
注意 只有当动态数组的数据位置为存储(storage)时,才可以使用成员函数 push(), push(x), 和 pop()。这三个函数都会影响数组的长度:
注意这三个成员函数会改变数组的长度
push 和 pop 函数示例
bash
uint[] arr; _//定义在storage位置的数组_
function pushPop() public {
_// 刚开始没有任何元素_
arr.push(); _// 数组有一个元素:[0]_
arr.push(1000); _//数组有两个元素:[0, 1000]_
arr.pop(); _// 数组剩下一个元素: [0]_
}
bash
uint[3] memory arr;
arr.push(1); _//编译错误,只有 storage 上的动态数组才能调用 push 函数_
arr.pop(); _// 编译错误,只有 storage 上的动态数组才能调用 pop 函数_
uint[] memory arr = new uint[](3);
arr.push(1); _//编译错误,只有 storage 上的动态数组才能调用 push 函数_
arr.pop(); _// 编译错误,只有 storage 上的动态数组才能调用 pop 函数_
多维数组
bash
uint[3][3] memory arr;
结构体
在 Solidity 中,有三种基本的引用类型:数组、结构体和映射类型。数组是将相同类型的元素集合到一起,形成一种新的数据类型。
结构体则是将不同类型的元素绑定在一起,创建出一种复合类型。结构体在 Solidity 中的应用非常广泛,具体体现在以下几个方面:
数据管理:结构体能够将多种不同类型的数据组合在一起,便于进行统一管理。
函数参数处理:通过结构体,我们可以将多个数据作为一个整体传入函数,无需将其拆解为多个独立参数。
返回值管理:同样地,结构体也可以用来从函数返回多个值,简化数据处理。
增强表达力:结构体的使用增强了 Solidity 的编程表达能力,因为结构体可以与其他结构体、数组或映射类型进行嵌套,从而使得代码结构更加清晰,易于理解。
定义一个结构体 在 Solidity 中定义一个结构体需要使用 struct 关键字。这允许你创建一个自定义的复合数据类型,集合不同的数据元素。下面是一个简单的例子,演示如何定义一个名为 Book 的结构体,其中包括书名和价格两个元素:
定义结构体类型
bash
struct Book {
string title; _// 书名_
uint price; _// 价格_
}
结构体的声明
bash
StructName DataLocation varName;
其中 StructName 是你定义的结构体名称, DataLocation 是数据位置。然后 varName 是给结构体变量取的任意名字。
bash
Book memory book;
结构体的初始化
在 Solidity 中,初始化结构体有两种常见方法。其中一种是通过键值对的形式,明确指定每个成员的初始值。这种方式为结构体的每个字段赋予明确的值,确保初始化过程清晰且易于理解。下面是一个示例:
bash
Book memory book1 = Book(
{
title: "my book title",
price: 25
}
);
bash
Book memory book2 = Book("my book title", 25);
获取结构体成员
bash
Book memory book3;
book3.title = "my book title"; _// 给结构体成员赋值_
book3.price = 25; _// 给结构体成员赋值_
console.log("title: %s", book3.title); _// 获取结构体成员值_
结构体可以和数组,映射类型互相嵌套
通过使用结构体,数据管理在 Solidity 中变得更加方便和高效。结构体不仅可以独立使用,还可以与数组和映射类型进行嵌套(映射类型将在下一节中详细介绍)。这种嵌套能力显著增强了 Solidity 的编程表达力和灵活性。如下所示:
结构体数组
bash
Book[] public lib; _// Book数组,状态变量_
function addNewBook(Book memory book) public {
lib.push(book);
}
结构体在 Solidity 中的应用非常灵活,其中还可以包含数组作为其成员。这为构建更复杂和动态的数据结构提供了可能。
以书籍为例,一本书可能有多个合著者。为了能够有效地存储这些信息,我们可以在书籍的结构体中添加一个数组来记录所有作者的名字。例如:
bash
struct Book {
string title; _// 书名_
uint price; _// 价格_
string[] author; _// 作者_
}
映射类型(mapping)
接下来我们看看如何声明一个映射类型的变量。其声明格式如下:
bash
mapping(KeyType => ValueType) varName;
在 Solidity 中,映射类型的声明涉及键(KeyType)和值(ValueType)。键的类型(KeyType)必须是内置的值类型,如 uint、string、bytes 等。重要的是要注意,键不能是数组、结构体或其他映射类型等引用类型。
如何使用 key 存取 value
如果你想通过 key 来存取对应 value , 可以使用 [] 操作符。
使用 [] 操作符
新增一个键值对
bash
airDrop[0xaaaaa...] = 100;
通过 key 获取 value
bash
unit amount = airDrop[0xbbbb...];
映射类型只能声明在 storage
bash
mapping(address => uint) memory myMap; _// 编译错误_
映射类型可以与数组,结构体互相嵌套
bash
struct Book {
uint isbn;
string title; _// 书名_
uint price; _// 价格_
}
mapping(uint => Book) lib; _// 从 ISBN 到 Book 的映射关系_