在查看示例合同时,有时数组是在具有“内存”的方法中声明的,有时则不是。有什么不同?
3 回答
如果没有memory关键字,Solidity 会尝试在storage中声明变量。
领导 Solidity 开发者克里斯思:“您可以将存储视为具有虚拟结构的大型数组……您无法在运行时更改的结构 - 它由合约中的状态变量决定”。
也就是说,存储结构在创建合约时根据你的合约级变量声明是一成不变的,并且不能被未来的方法调用更改。但是——可以通过 sendTransaction 调用更改该存储的内容。这样的调用会改变“状态”,这就是为什么合约级别的变量被称为“状态变量”。所以一个变量 uint8 storagevar; 在合约级别声明可以更改为 uint8 (0-255) 的任何有效值,但 uint8 类型的值的“槽”将始终存在。
如果你在函数中声明变量时没有使用memory关键字,solidity 将尝试使用当前可以编译的存储结构,但会产生意想不到的结果。memory告诉solidity 在方法运行时为变量创建一块空间,保证它的大小和结构以供将来在该方法中使用。
内存不能在合约级别使用。仅在方法中。
请参阅条目“什么是 memory 关键字?它有什么作用?” 在常见问题解答中。我在这里引用它:
以太坊虚拟机具有三个可以存储项目的区域。
第一个是“存储”,所有合约状态变量都驻留在其中。每个合约都有自己的存储,并且在函数调用之间是持久的,并且使用起来非常昂贵。
第二个是“内存”,用于保存临时值。它在(外部)函数调用之间被删除并且使用成本更低。
第三个是栈,用来存放小的局部变量。它几乎可以免费使用,但只能保存有限数量的值。
对于几乎所有类型,您无法指定它们的存储位置,因为它们每次使用时都会被复制。
所谓存储位置很重要的类型是结构和数组。例如,如果您在函数调用中传递此类变量,则如果它们的数据可以保留在内存中或保留在存储中,则不会复制它们的数据。这意味着您可以在被调用函数中修改它们的内容,并且这些修改在调用者中仍然可见。
存储位置有默认值,具体取决于它涉及的变量类型:
- 状态变量总是在存储中
- 函数参数总是在内存中
- 结构、数组或映射类型的局部变量默认引用存储
- 值类型的局部变量(即既不是数组,也不是结构,也不是映射)存储在堆栈中
存储在函数调用之间保存数据。它就像一个电脑硬盘。状态变量是存储数据。这些状态变量位于区块链上的智能合约数据部分。
内存是存储数据的临时场所,例如 RAM。函数中的函数参数和局部变量是内存数据。以太坊虚拟机的内存空间有限,因此存储在此处的值会在函数调用之间被擦除。
假设我们要修改函数内部的顶级状态变量。
// state variables are placed in Storage
// I am gonna mutate this inside the function
int[] public numbers
function Numbers()public{
numbers.push(5)
numbers.push(10)
int[] storage myArray=numbers
// numbers[0] will also be changed to 1
myArray[0]=1
//Imagine you have an NFT contract and store the user's purchased nfts in a state variable on top-level
// now inside a function maybe you need to delete one of the NFT's, since user sold it
// so you will be modifying that list, inside a function using "storage"
}
int[] storage myArray=numbers
在这种情况下,myArray 将指向与“数字”相同的地址(它类似于引用对象在 javascript 中的行为方式)。在函数中,我将 5,然后 10 添加到放入存储中的“数字”。但是如果你在 remix 和 get 上部署代码numbers[0]
,你会得到 1 因为myArray[0]=1
如果您将其定义myArray
为记忆,那将是另一回事。
// state variables are placed in Storage
int[] public numbers
function Numbers() public{
numbers.push(5)
numbers.push(10)
// we are telling Solidity make numbers local variable using "memory"
// That reduces gas cost of your contract
int[] memory myArray=numbers
myArray[0]=1
// Now, this time maybe you want to user's NFT's where price is less than 100 $
// so you create an array stored in "memory" INSIDE the function
// You loop through user's Nft's and push the ones that price<100
// then return the memory variable
// so, after you return the memory variable, it will be deleted from the memory
}
在这种情况下,“numbers”数组被复制到内存中,myArray 现在引用了一个与“numbers”地址不同的内存地址。如果您部署此代码并到达numbers[0]
,您将获得 5。
我展示了一个简单函数的区别,因此可以在 Remix 上轻松测试它
memory
定义 Solidity 中可以在运行时临时保存值的数据位置之一。memory
Solidity 中的变量只能在方法中声明,通常用在方法参数中。这是一个短期变量,不能保存在区块链上;它仅在函数执行期间保存该值,并且其值在执行后被销毁。
看一下我使用关键字f()
声明指针的示例函数。memory
它不会改变 variable 的值User
,而如果使用它声明storage
它会改变User
存储在区块链上的 variable 的值,并且该值不会被破坏......
struct User {
string name;
}
User[] users;
function f() external {
User memory user = users[0]; // create a pointer
user.name = "example name" // can't change the value of struct User
}