数据地址
每个引用类型都有一个附加注释,即“数据位置”,关于它的存储位置。共有三个数据位置 memory:storage和calldata。Calldata 是存储函数参数的不可修改、非持久性区域,其行为主要类似于内存。
笔记
如果可以,请尝试calldata用作数据位置,因为它可以避免复制并确保无法修改数据。具有数据位置的数组和结构calldata 也可以从函数返回,但不可能分配此类类型。
笔记
在 0.6.9 版之前,引用类型参数的数据位置仅限 calldata于外部函数、memory公共函数以及 memory内部storage和私有函数中。现在memory,calldata无论其可见性如何,都允许在所有功能中使用。
笔记
在 0.5.0 版本之前,数据位置可以省略,并且会根据变量的类型、函数类型等默认到不同的位置,但现在所有复杂类型都必须给出明确的数据位置。
数据位置和分配行为
数据位置不仅与数据的持久性有关,还与分配的语义有关:
-
storage和memory(或 from )之间的分配calldata总是创建一个独立的副本。 -
分配 from
memorytomemory仅创建参考。这意味着对一个内存变量的更改在引用相同数据的所有其他内存变量中也可见。 -
storage对本地存储变量的赋值也只分配一个引用。 -
所有其他分配
storage始终复制。这种情况的示例是对状态变量或存储结构类型的局部变量成员的赋值,即使局部变量本身只是一个引用。
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.5.0 <0.9.0;
contract C {
// The data location of x is storage.
// This is the only place where the
// data location can be omitted.
uint[] x;
// The data location of memoryArray is memory.
function f(uint[] memory memoryArray) public {
x = memoryArray; // works, copies the whole array to storage
uint[] storage y = x; // works, assigns a pointer, data location of y is storage
y[7]; // fine, returns the 8th element
y.pop(); // fine, modifies x through y
delete x; // fine, clears the array, also modifies y
// The following does not work; it would need to create a new temporary /
// unnamed array in storage, but storage is "statically" allocated:
// y = memoryArray;
// This does not work either, since it would "reset" the pointer, but there
// is no sensible location it could point to.
// delete y;
g(x); // calls g, handing over a reference to x
h(x); // calls h and creates an independent, temporary copy in memory
}
function g(uint[] storage) internal pure {}
function h(uint[] memory) public pure {}
}