solidity基础-数据位置
基础介绍
- storage:状态变量的保存位置,存储在链上,存储成本比较高(比较费gas)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract Demo {
uint8 public age = 18;
uint256 a = 1;
uint256[] arr = [1, 2, 3];
}
memory:数据保存在内存中,仅在生命周期内有效
- 使用的时候分配,超过作用域不可访问,等待回收
- memory 只能修饰引用数据类型,不能修饰值类型
- 函数参数(包括返回值)都存储在内存中
引用类型的局部变量,需要显式地指定数据位置
- 不能在内存中创建动态数据
- mapping 和 struct 不能在函数中动态创建,必须从状态变量中分配
- 函数的输入和输出参数,如果是引用类型,则需要使用 memory 标识(callback)
引用类型的局部变量:指定 storage / memory 的区别
- storage 修改引用数据,会修改状态变量
- memory 修改引用数据,函数运行完就消失了,修改的值也不会储存在状态中
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract Demo {
function add(uint256 num1, uint256 num2) public pure returns (uint256 sum) {
uint256 tmp = num1 + num2; // tmp 存储在 memory 中
return tmp;
}
}
calldata:保存函数参数的特殊数据位置,是一个只读的位置
- calldata 只能用在函数的输入和输出参数中
- calldata 用在输入参数中,比 memory 更省 gas
- calldata 标记的参数不可被修改, memory 标记的参数是可以修改的
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract Demo {
function test(uint256[] calldata arr) external pure returns (uint256[] calldata) {
// arr 不可修改
return arr;
}
}
不同数据位置之间的赋值规则
存储变量 => 存储变量
- 值类型:创建一个新的副本
- 引用类型:创建一个新的副本
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract Demo {
uint256 public state1 = 10;
uint256 public state2 = 20;
string public str1 = "ABCD";
string public str2 = "1234";
function testA() public returns (uint256) {
state1 = state2;
state2 = 30;
return state1; // 第一次触发返回20 第二次触发返回30
}
function testB() public returns (string memory) {
str1 = str2;
bytes(str2)[0] = bytes1("9");
return str1; // 第一次触发返回 '1234' 第二次触发返回 '9234'
}
}
存储变量 => 内存变量
- 值类型:创建一个新的副本
- 引用类型:创建一个新的副本
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract Demo {
uint256 public state1 = 10;
string public str1 = "ABCD";
function testA() public returns (uint256) {
uint256 state2 = 20;
state2 = state1;
state1 = 30;
return state2; // 第一次触发返回10 第二次触发返回30
}
function testB() public returns (string memory) {
string memory str2 = "1234";
str2 = str1;
bytes(str1)[0] = bytes1("9");
return str2; // 第一次触发返回 'ABCD' 第二次触发返回 '9BCD'
}
}
内存变量 => 内存变量
- 值类型:创建一个新的副本
- 引用类型:传递指针
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract Demo {
function testA() public returns (uint256) {
uint256 state1 = 10;
uint256 state2 = 20;
state1 = state2;
state2 = 30;
return state1; // 返回值永远是20
}
function testB() public returns (string memory) {
string memory str1 = "ABCD";
string memory str2 = "1234";
str1 = str2;
bytes(str2)[0] = bytes1("9");
return str1; // 返回值永远是 '9234'
}
}
内存变量 => 存储变量
- 值类型:创建一个新的副本
- 引用类型:创建一个新的副本
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract Demo {
uint256 public state1 = 10;
string public str1 = "ABCD";
function testA() public returns (uint256) {
uint256 state2 = 20;
state1 = state2;
state2 = 30;
return state1; // 返回值永远是 20
}
function testB() public returns (string memory) {
string memory str2 = "1234";
str1 = str2;
bytes(str2)[0] = bytes1("9");
return str1; // 返回值永远是 '1234'
}
}
案例
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract Demo {
string public state1 = "ABC";
struct MyStruct {
string name;
uint8 age;
}
mapping(address => MyStruct) public state2;
constructor() {
state2[msg.sender] = MyStruct({name: "b00l", age: 18});
}
function test1() public returns (string memory, string memory) {
string memory memory1 = "1234";
state1 = memory1;
string storage storage1 = state1;
bytes(storage1)[0] = bytes1("9");
return (state1, storage1); // 9234 9234
}
function test2() public returns (string memory, string memory) {
string memory memory1 = "1234";
state1 = memory1;
string memory memory2 = state1;
bytes(memory2)[0] = bytes1("9");
return (state1, memory2); // 1234 9234
}
function test3() external returns (MyStruct memory, MyStruct memory) {
MyStruct memory memory1 = MyStruct({name: "b00l2", age: 20});
state2[msg.sender] = memory1;
MyStruct storage storage1 = state2[msg.sender];
storage1.age++;
return (state2[msg.sender], storage1); // b00l2, 21 b00l2, 21
}
function test4() external returns (MyStruct memory, MyStruct memory) {
MyStruct memory memory1 = MyStruct({name: "b00l2", age: 20});
state2[msg.sender] = memory1;
MyStruct memory memory2 = state2[msg.sender];
memory2.age++;
return (state2[msg.sender], memory2); // b00l2, 20 b00l2, 21
}
}
calldata 和 memory 的区别
- calldata 可以隐式地转换为 memory
- memory 不能隐式地转换为 calldata