基础
默认数据位置
Copy 状态变量默认storage
内部函数参数默认memory
外部函数参数默认calldata
例如
Copy pragma solidity ^0.8.0;
contract SimpleAssign{
// storage 类型
struct S{
string a;
uint b;
}
function assign(S s) internal{
// 函数参数默认是 memory 类型,即 s 是 memory 类型
// 局部变量默认 storage 类型
// 将一个memory类型的参数赋值给storage类型的局部变量会报错。
S tmp = s; // 报错
}
}
值转换
storage–>storage
把一个storage类型赋值给一个 storage 类型时,只是修改其指针(引用传递)。
Copy pragma solidity ^0.8.0;
contract StorageToStorageTest{
struct S{string a;uint b;}
//默认是storage的
S s;
function storageTest(S storage s) internal{
S test = s;
test.a = "Test";
}
function call() returns (string){
storageTest(s);
return s.a; //Test
}
}
memory–>storage
分为 2 种情况: a. 将 memory–>状态变量; 即将内存中的变量拷贝到存储中(值传递)
Copy pragma solidity ^0.8.0;
contract StorageToStorageTest{
struct S{string a;uint b;}
//默认是storage
S s;
function storageTest(S s) internal{
s = s;
s.a = "Test";
}
function call() returns (string){
storageTest(s);
return s.a;
}
}
b.将memeory–>局部变量 报错
storage–>memory
即将数据从storage拷贝到memory中
Copy pragma solidity ^0.4.0;
contract StorageToMemory{
struct S{string a;uint b;}
// storage 类型
S s = S("storage", 1);
function storageToMemory(S storage x) internal{
S memory tmp = x;//由Storage拷贝到memory中
//memory的修改不影响storage
tmp.a = "Test";
}
function call() returns (string){
storageToMemory(s);
return s.a;//storage
}
}
memory–>memory
和storage转storage一样是引用传递
Copy pragma solidity ^0.4.0;
contract MemoryToMemory{
struct S{string a;uint b;}
function smemoryTest(S s) internal{
S memory test = s;
test.a = "Test";
}
function call() returns (string){
//默认是storage的
S memory s = S("memory",10);
smemoryTest(s);
return s.a;//Test
}
}
Memory
概述
存储在内存中,即分配、即使用,越过作用域则不可访问,等待被回收。 内存是易失性读写字节可寻址空间。它主要用于在执行过程中存储数据,主要用于向内部函数传递参数。鉴于这是易失性区域,每个消息调用都以清除内存开始。所有位置最初都定义为零。作为calldata,内存可以按字节级别寻址,但一次只能读取32字节的字。 当我们写入以前未使用过的单词时,内存被称为“扩展”。除了写入本身的成本之外,这种扩展也有成本,前 724 字节呈线性增长,之后呈二次方增长。
特点
Copy 存储在区块链
状态变量
复杂类型的局部变量
Storage对应合约内的存储。
你可以把你不想被cloned的变量作为Storage存储。
Storage是长期有效的,但是成本比较高。
操作码
EVM 提供了三个操作码来与内存区域进行交互
Copy MLOAD 从内存中加载一个单词到堆栈中。
MSTORE 将单词保存到内存中。
MSTORE8 将一个字节保存到内存中。
Calldata
概述
存储函数参数,它是只读的,不会永久存储的一个数据位置。外部函数的参数被强制指定为 calldata,效果与 memory 类似。
特点
Copy calldata几乎是免费的,但是有一个长度的限制。
calldata 是一个只读的字节可寻址空间,其中保存了事务或调用的数据参数。与堆栈不同,要使用此数据,您必须指定确切的字节偏移量和要读取的字节数。
操作码
EVM 提供的用于操作调用数据的操作码
Copy CALLDATASIZE 告诉交易数据的大小。
CALLDATALOAD 将 32 字节的事务数据加载到堆栈中。
CALLDATACOPY 将一定数量的交易数据字节复制到内存中。
常用操作
Memory变量
Memory变量临时变量(内存型),合约函数执行完成时,内存型变量被移除。 在函数参数或返回值声明时,如果返回数据的类型是变长的,那么需要加memory修饰,例如
Copy string、
bytes、
数组、
自定义结构
等类型都需要使用memory。
Copy function setUser(string memory _name, uint8 _age, string memory _sex) public {}
storage变量
storage的作用类似于C++中的引用传递,用storage修饰的变量等同于右值的一个分身,对其进行修改也会影响到本尊。
区别
如果数组中的结构体赋值给函数中声明的变量,使用memory修饰获得的是一个副本,也就是拷贝
Copy function edit(uint _id) public {
//这里获取的是 一份拷贝,下面的修改不会影响数组里的结构体
Person memory p = persons[_id];
p.name = "test";
p.age = 0;
//执行结束后 persons[_id]里的name 和age并不会改变
}
如果使用storage修饰变量,那么得到的是一个引用,就是p 是指向 persons[_id] 的指针。例如
Copy function edit(uint _id) public {
Person storage p = persons[_id];
//这里会改变数组里的结构体
p.name = "test";
p.age = 0;
//执行结束后,persons[_id]的name和age分别变为 "test"和0
}