[英]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.