簡體   English   中英

在 Ethereum Solidity 中,“memory”關鍵字的作用是什么?

[英]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 中談論StorageMemory時,實際上可以指代這兩個詞的兩種不同用法 這會引起很多混亂。

兩種用途是:

  1. Solidity 合約存儲數據的位置
  2. Solidity 變量如何存儲值

每個示例:

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM