本文详细探讨了Solidity中的数组,介绍了动态数组与固定数组的区别,以及存储数组与内存数组的特点和用法。作者还分析了映射与数组的比较,以及如何声明和初始化数组。此外,文章提供了对Solidity数组的常见错误的故障排除建议,适合希望深入了解编程语言的开发者。
数组是线性数据结构,用于存储固定大小的相同数据类型元素,这些元素存储在连续且相邻的内存位置中。数组中的每个元素都通过其位置来指定,通常称为其索引。
数组基于索引系统工作,索引从0开始到(n-1),其中n表示数组的总体大小。与创建几个相同类型的单独变量相对,开发人员只需要声明一个所需大小的数组,从而存储可以通过索引访问的元素。
在Solidity中,主要用于以太坊和与EVM兼容区块链的web3编程语言,数组可以是固定大小或动态大小。作为一种面向对象的编程语言,Solidity支持智能合约之间的继承,多个合约可以继承到一个单一合约中。
如果你想深入了解Solidity数组,请注册Alchemy大学的免费Solidity开发者课程。
动态数组在声明时大小不是预定义的,而固定数组具有预定义的大小。 随着元素的系统性添加,动态数组的大小会变化,在运行时,数组的实际大小将被确定。
相反,固定数组具有预定义的大小,数组中存在的元素数量不应超过数组的大小。如果在不指定数组大小的罕见情况下,则会创建一个“足够大小”的数组。一个“足够大小”的数组,其大小“足够”来容纳初始化。
在Solidity中,开发者需要考虑两种类型的数组:存储数组和内存数组。
存储数组 通常被声明为状态变量,可以是固定或动态大小。值得注意的是,动态大小的存储数组可以被调整大小,这意味着它们可以访问push()和pop()函数,分别用于添加和移除数组中的元素。
内存数组 以内存作为其数据位置进行声明。与存储数组类似,内存数组在编译时也可以具有固定或动态长度,但在内存分配后无法调整大小。这意味着无法使用pop()和pull() Solidity函数。
固定大小的内存数组在你声明它们时会被自动分配- 例如语句uint256[5] memory numbers
。然而,要分配动态内存数组,你需要使用new运算符。例如,你可以使用uint256[] memory numbers = new uint256[](5)
。
注意: 使用数组之前,总是要初始化它,以便可以获得有效的地址进行使用。
Solidity中的映射类似于数组,因为它是一个引用类型,用于存储一组数据。映射的语法和结构截然不同,使其服务于独特且重要的目的。映射是一个键值对表,每个都有自己预定义的类型。映射可以被视为在一个Solidity智能合约内初始化的空表,等待填充数据。
与数组不同,映射没有可检索的长度,键或值在初始化阶段不必被“设置”。此外,映射不能像Solidity数组那样进行循环。
然而,从映射中获取一段数据远比从数组中获取相同的数据高效。要从数组中提取数据,需要遍历整个数组,直到找到你要查找的特定元素,而映射将立即抓取该数据。
从映射中提取数据的性能优势对于节省以太坊智能合约的Gas费用可能非常重要,因为编辑区块链上的数据的事务需要支付Gas费用。因此,尽可能高效地存储或从智能合约中检索数据可能会随着时间的推移为开发人员节省ETH。
总之,如果你需要迭代一组数据,比如使用for循环,那么请使用数组。如果不需要迭代一组数据,而是可以基于已知键检索值,那么考虑使用映射。
在Solidity中声明数组相对简单。应指定元素的数据类型和元素的数量,数组的大小应为正整数,并且数据类型在Solidity中应有效。
例如,以下基本指令将初始化数组,并且一旦数据被插入,它将在console.log上显示:
uint256 public constant arraySize = 5; uint256[] public numbers = new uint256[](arraySize);
数组中的每个项称为元素,每个元素可以通过其数字索引访问,索引公式为“n - 1”,其中n代表元素编号。
一般而言,与元素相关的编号从0开始。这意味着第10个元素将在索引9处访问,第11个元素则在索引10处,以此类推。
关于相同数据类型的限制是重要的,因为数组存储在连续的内存单元中,这意味着每个单元必须是相同类型,因此,大小也应相同。
结构体允许程序员定义自己的数据类型。一旦定义了结构体,它可以作为状态变量使用,或者在多种其他函数中使用,采用位置参数或关键字。第二种方法避免了记住结构体成员顺序的问题。
以下是一个新结构体的示例:
struct Transfer {
uint256 date;
uint256 value;
}
映射可以被视为一个键值存储,其中每个可能的键都存在,并且可以通过键设置或检索任何值。KeyType可以是任何内置值类型(即bytes,string或任何合约/枚举类型)。ValueType可以是任何类型,包括映射、数组和结构体。
映射可以如下声明:
mapping( ⇒ )
注意:映射变量唯一允许的存储位置为存储。
找到数组的特定成员意味着在数组中搜索直到找到该成员。开发人员需要注意两个重要函数:length
和push
。
Length - 返回数组的大小,也可以用于更改动态数组的大小。
Push - 使开发者能够将元素直接附加到动态存储数组的末尾,因此返回数组的新长度。
偶尔,开发人员可能会遇到编译错误,这可能源于对声明、创建和初始化数组规则的误解。以下是一些高层次的方法来故障排除常见的Solidity数组错误:
array.length
函数允许开发人员检查数组中存在的元素数量。内存数组在声明时大小是固定的,而如果在运行时定义动态数组,则需要长度进行操作。
当在外部合约上调用函数时,EVM计算一个包含函数签名和参数的字节缓冲区,有两种方式来序列化参数:abi.encode
和 abi.encodePacked
。
Abi.encode使用ABI规范对其参数进行编码。实际上,ABI旨在使调用合约时,参数填充到32字节。如果正在调用合约,则极有可能使用此函数。
相比之下,abi.encodePacked使用按类型所需的最小空间对其参数进行编码。编码一个uint8将使用1个字节,且在你希望节省一些空间且不打算调用合约时使用。
这两种数组类型可以保存任意长度的原始字节数据。byte[]
和 bytes
的区别在于 bytes[]
遵循数组类型的规则,并且Solidity中的内存数组的元素总是占据32字节的倍数。这意味着,如果一个元素少于32字节的倍数,则会进行内存填充,直至符合必要的大小。例如,在字节数组的情况下,可能会浪费31个字节,但使用字节数组或字符串时不会出现这种情况。
该动态数组是UTF-8数据类型,功能上与其他编程语言不同,Solidity不提供获得字符串长度或进行两个字符串的连接或比较的函数。字符串可以快速转换为字节数组,使用bytes[]
。
尽管在其他编程语言(如Python和JavaScript)中存在未定义、空值、_无_等内在概念,但在Solidity中不存在。相反,开发人员可以称其为零或默认值的概念,因为每个值在创建后都会在内存中占据一个槽,因此它应该包含某些内容。
- 原文链接: alchemy.com/overviews/so...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!