101

在查看示例合同时,有时数组是在具有“内存”的方法中声明的,有时则不是。有什么不同?

4

3 回答 3

127

如果没有memory关键字,Solidity 会尝试在storage中声明变量。

领导 Solidity 开发者克里斯思:“您可以将存储视为具有虚拟结构的大型数组……您无法在运行时更改的结构 - 它由合约中的状态变量决定”。

也就是说,存储结构在创建合约时根据你的合约级变量声明是一成不变的,并且不能被未来的方法调用更改。但是——可以通过 sendTransaction 调用更改该存储的内容。这样的调用会改变“状态”,这就是为什么合约级别的变量被称为“状态变量”。所以一个变量 uint8 storagevar; 在合约级别声明可以更改为 uint8 (0-255) 的任何有效值,但 uint8 类型的值的“槽”将始终存在。

如果你在函数中声明变量时没有使用memory关键字,solidity 将尝试使用当前可以编译的存储结构,但会产生意想不到的结果。memory告诉solidity 在方法运行时为变量创建一块空间,保证它的大小和结构以供将来在该方法中使用。

内存不能在合约级别使用。仅在方法中。

请参阅条目“什么是 memory 关键字?它有什么作用?” 在常见问题解答中。我在这里引用它:

以太坊虚拟机具有三个可以存储项目的区域。

第一个是“存储”,所有合约状态变量都驻留在其中。每个合约都有自己的存储,并且在函数调用之间是持久的,并且使用起来非常昂贵。

第二个是“内存”,用于保存临时值。它在(外部)函数调用之间被删除并且使用成本更低。

第三个是栈,用来存放小的局部变量。它几乎可以免费使用,但只能保存有限数量的值。

对于几乎所有类型,您无法指定它们的存储位置,因为它们每次使用时都会被复制。

所谓存储位置很重要的类型是结构和数组。例如,如果您在函数调用中传递此类变量,则如果它们的数据可以保留在内存中或保留在存储中,则不会复制它们的数据。这意味着您可以在被调用函数中修改它们的内容,并且这些修改在调用者中仍然可见。

存储位置有默认值,具体取决于它涉及的变量类型:

  • 状态变量总是在存储中
  • 函数参数总是在内存中
  • 结构、数组或映射类型的局部变量默认引用存储
  • 值类型的局部变量(即既不是数组,也不是结构,也不是映射)存储在堆栈中
于 2015-11-21T02:50:17.233 回答
9
  • 存储在函数调用之间保存数据。它就像一个电脑硬盘。状态变量是存储数据。这些状态变量位于区块链上的智能合约数据部分。

  • 内存是存储数据的临时场所,例如 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 上轻松测试它

于 2021-09-15T21:00:35.563 回答
8

memory定义 Solidity 中可以在运行时临时保存值的数据位置之一。memorySolidity 中的变量只能在方法中声明,通常用在方法参数中。这是一个短期变量,不能保存在区块链上;它仅在函数执行期间保存该值,并且其值在执行后被销毁。

看一下我使用关键字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
}
于 2021-06-13T13:27:05.690 回答