[英]In Ethereum Solidity, what is the purpose of the "memory" keyword?
在查看示例合同时,有时 arrays 在具有“内存”的方法中声明,有时则不是。 有什么不同?
如果没有memory关键字,Solidity 会尝试在storage 中声明变量。
首席 Solidity 开发人员 chriseth:“您可以将存储视为具有虚拟结构的大型阵列……一种您无法在运行时更改的结构 - 它由合约中的状态变量决定”。
也就是说,存储结构在合约创建时基于你的合约级变量声明是一成不变的,并且不能被未来的方法调用改变。 但是——该存储的内容可以通过 sendTransaction 调用进行更改。 这种调用会改变“状态”,这就是合约级变量被称为“状态变量”的原因。 所以一个变量 uint8 storagevar; 在合约级别声明的可以更改为 uint8 (0-255) 的任何有效值,但 uint8 类型值的“槽”将始终存在。
如果在函数中声明变量时没有使用memory关键字,那么 Solidity 将尝试使用当前编译的存储结构,但会产生意外结果。 memory告诉 Solidity 在方法运行时为变量创建一块空间,保证它的大小和结构供将来在该方法中使用。
内存不能在合约级别使用。 只在方法上。
请参阅条目“内存关键字是什么?它有什么作用?” 在常见问题解答中。 我在这里引用:
以太坊虚拟机具有三个可以存储物品的区域。
第一个是“存储”,所有合约状态变量都在其中。 每个合约都有自己的存储空间,并且在函数调用之间是持久的,并且使用起来非常昂贵。
第二个是“内存”,用于保存临时值。 它在(外部)函数调用之间被擦除,使用起来更便宜。
第三个是栈,用来存放小的局部变量。 它几乎可以免费使用,但只能保存有限数量的值。
对于几乎所有类型,您都无法指定它们的存储位置,因为每次使用它们时都会复制它们。
所谓的存储位置很重要的类型是结构和数组。 例如,如果您在函数调用中传递此类变量,则它们的数据如果可以保留在内存中或保留在存储中,则不会被复制。 这意味着您可以在被调用的函数中修改它们的内容,并且这些修改在调用者中仍然可见。
存储位置有默认值,具体取决于它涉及的变量类型:
- 状态变量总是在存储中
- 函数参数总是在内存中
- struct、array或mapping类型的局部变量默认引用存储
- 值类型的局部变量(即既不是数组,也不是结构体也不是映射)存储在堆栈中
memory
定义了 Solidity 中的数据位置之一,它只能在运行时临时保存值。
Solidity 中的memory
变量只能在方法中声明,通常用于方法参数中。 一个不能保存在区块链中的短期变量......它只在函数执行期间保存值,并且它的值在执行后销毁。
看一下示例函数f()
,其中我使用memory
关键字声明了一个指针……它不会改变变量User
的值,而如果使用storage
声明它,它将更改存储在区块链中的变量User
的值和 value不会被破坏...
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
}
当人们在 Solidity 中谈论Storage和Memory时,实际上可以指代这两个词的两种不同用法。 这会引起很多混乱。
两种用途是:
每个示例:
1. Solidity 合约存储数据的位置:正如 Yilmaz 正确指出的那样,在第一次使用时,存储和 memory 可以被认为分别类似于硬盘驱动器(长期、持久存储)和 RAM(临时)。
例如:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract StorageMemory1{
uint storageVariable;
constructor() {
}
function assignToValue(uint memoryVariable) public {
storageVariable = memoryVariable;
}
}
在上面的例子中,'storageVariable' 的值将被保存,即使我们随着时间的推移执行不同的函数。 但是,当调用“assignToValue”function 时会创建“memoryVariable”,然后在 function 完成后永远消失。
2. Solidity 变量如何存储值:如果您看到类似“数据位置必须是变量的“存储”、“内存”或“调用数据”之类的错误,但没有给出。 那么这就是它所指的。 使用示例可以最好地理解这一点。
例如:
您将使用以下代码收到上述错误:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract StorageMemory2 {
uint[] public values;
function doSomething() public
{
values.push(5);
values.push(10);
uint[] newArray = values; // The error will show here
}
}
但如果你加上“记忆”这个词:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import 'hardhat/console.sol'; // to use console.log
contract StorageMemory2 {
uint[] public values;
function doSomething() public
{
values.push(5);
values.push(10);
console.log(values[0]); // it will log: 5
uint[] storage newArray = values; // 'newArray' references/points to 'values'
newArray[0] = 8888;
console.log(values[0]); // it will log: 8888
console.log(newArray[0]); // it will also log: 8888
}
}
注意添加单词 'storage' 的作用:它使 'newArray' 变量引用(或指向) 'values' 变量,并且修改 'newArray' 也会修改 'values' 。
但是,如果我们改为使用 'memory' ,请注意记录的内容:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import 'hardhat/console.sol'; // to use console.log
contract StorageMemory2 {
uint[] public values;
function doSomething() public
{
values.push(5);
values.push(10);
console.log(values[0]); // it will log: 5
uint[] memory newArray = values; // 'newArray' is a separate copy of 'values'
newArray[0] = 8888;
console.log(values[0]); // it will log: 5
console.log(newArray[0]); // it will log: 8888
}
}
使用 memory 创建一个复制变量,它不引用“值”数组。
如果您有兴趣,可以使用“ calldata ”以只读方式传递变量:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
contract CallDataExample {
uint[] public values;
function doSomething() public
{
values.push(5);
values.push(10);
modifyArray(values);
}
function modifyArray(uint[] calldata arrayToModify) pure private {
arrayToModify[0] = 8888; // you will get an error saying the array is read only
}
}
存储保存函数调用之间的数据。 它就像电脑硬盘。 变量是存储数据
内存临时存放数据的地方,比如 RAM。 函数参数是内存数据
假设一个数组是一种存储类型
// state variables are placed in Storage
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
}
int[] storage myArray=numbers
在这种情况下 myArray 将指向与“numbers”相同的地址。 (它类似于引用对象在 javascript 中的行为方式)。 请参阅我在函数中添加 5,然后将 10 添加到放入存储中的“数字”。 但是如果你在 remix 上部署代码并得到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)
int[] memory myArray=numbers
myArray[0]=1
}
在这种情况下,“数字”数组被复制到内存中,而 myArray 现在引用了一个与“数字”地址不同的内存地址。 如果您部署此代码并达到numbers[0]
您将获得 5。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.