【问题标题】:In Ethereum Solidity, what is the purpose of the "memory" keyword?在 Ethereum Solidity 中,“memory”关键字的作用是什么?
【发布时间】:2016-02-23 16:14:53
【问题描述】:

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

【问题讨论】:

    标签: ethereum solidity


    【解决方案1】:

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

    Lead Solidity dev chriseth:“您可以将存储视为具有虚拟结构的大型数组......您无法在运行时更改的结构 - 它由合约中的状态变量决定”。

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

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

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

    请参阅常见问题解答中的the entry "What is the memory keyword? What does it do?"。我在这里引用它:

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

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

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

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

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

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

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

    • 状态变量始终在存储中
    • 函数参数始终在内存中
    • 结构、数组或映射类型的局部变量默认引用存储
    • 值类型的局部变量(即既不是数组,也不是结构,也不是映射)存储在堆栈中

    【讨论】:

    • 您是否有任何指向解释此问题的文档的链接?我想了解更多有关存储如何工作的信息。
    • @Acapuclo 在常见问题解答中“内存关键字是什么?它有什么作用?”
    • 常见问题链接不起作用,但如果您想阅读类似的链接,我建议docs.soliditylang.org/en/v0.5.3/…
    • 我读过它,但仍然需要初学者对此进行解释,所以基本上为了避免昂贵的操作(节省存储空间)我们应该在函数参数之前使用 memory 关键字?如果内存是短暂的,那么使用它的原因是什么?合约如何在部署后仍然调用这些函数并因此修改内存?
    • 作为一个没有使用过 Solidity 的人,默认情况下变量不会在内存中并且持久化它们是需要明确的事情,这似乎很奇怪
    【解决方案2】:

    memory 定义了 Solidity 中的数据位置之一,它只能在运行时临时保存值。
    Solidity 中的memory 变量只能在方法中声明,通常用于方法参数。一个不能保存在区块链中的短期变量......它只在函数执行期间保存值,并且它的值在执行后销毁。

    看看示例函数f(),其中我使用memory关键字声明了一个指针。它不会改变变量User的值,而如果它是使用storage声明的,它将改变变量User的值存储在区块链中,值不会被销毁...

    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
    }
    

    【讨论】:

      【解决方案3】:
      • 存储在函数调用之间保存数据。它就像电脑硬盘。变量是存储数据

      • 内存临时存储数据的地方,如 RAM。函数 args 是内存数据

      假设一个数组是一种存储类型

      // 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 将指向与“数字”相同的地址。 (它类似于引用对象在 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 
      }
      

      在这种情况下,“numbers”数组被复制到内存中,myArray 现在引用了一个不同于“numbers”地址的内存地址。如果您部署此代码并到达numbers[0],您将获得 5。

      【讨论】:

      • 因为 int[] storage myArray 只是一个指向 numbers 变量的指针,并且没有为 myArray 保留存储空间。将 myArray 分配给数字的 gas 成本是多少?
      • 另外,myArray 是一个存储引用,那么这个指针是存储在内存中还是存储本身?
      猜你喜欢
      • 1970-01-01
      • 2022-12-22
      • 2019-08-12
      • 2018-06-13
      • 1970-01-01
      • 1970-01-01
      • 2018-11-12
      • 2015-10-13
      • 2016-06-03
      相关资源
      最近更新 更多