目录

calldata

memory

storage

三者之间的转换

storage作为参数,赋值到memory

(1)

(2)

(3)

storage作为参数,赋值给storage

memory作为参数,赋值给memory

memory作为参数,赋值给storage

calldata

官方文档对calldata的描述:

Calldata is a non-modifiable, non-persistent area where function arguments are stored, and behaves mostly like memory.

翻译:Calldata是一个不可修改的、非持久化的区域,函数参数存储在这里,其行为主要类似于内存。

它只能用于函数声明参数(而不是函数逻辑) 它是不可变的(不能被覆盖和更改),调用数据避免了数据拷贝,并确保数据不被修改 它必须用于external函数的动态参数 它是临时的(该值在事务完成后会销毁) 它是最便宜的存储位置,一般建议将函数参数声明为calldata,因为gas费会比较低。 是const 外部函数的参数(不包括返回参数)被强制指定为calldata

memory

简介:在合约中的本地内存变量。它的生命周期很短,当函数执行结束后就销毁了

内存是一个字节数组,内存槽为256位(32字节)数据仅在函数执行期间存在,执行完毕后就被销毁,读或写一个内存槽都会消耗3gas为了避免矿工的工作量过大,22个操作之后的单操作成本会上涨

storage

简介:在合约中可以被所有函数访问的全局变量。storage是永久的存储,意味着以太坊会把它保存到公链环境里的每一个节点

存储中的数据是永久存在的。存储是一个key/value库- 存储中的数据写入区块链,因此会修改状态,这也是存储使用成本高的原因。

占用一个256位的存储槽需要消耗20000 gas,修改一个已经使用的存储槽的值,需要消耗5000 gas,当清零一个存储槽时,会返还一定数量的gas,存储按256位的槽位分配,即使没有完全使用一个槽位,也需要支付其开销

三者之间的转换

storage作为参数,赋值到memory

(1)

pragma solidity ^0.4.24;

contract Person {

int public _age;

constructor (int age) public {

_age = age;

}

function f() public view{

modifyAge(_age);

}

function modifyAge(int age) public pure{

age = 100;

}

}

分析

在这里一开始deploy合约时,传入的age值为30,此时_age的值为30然后运行f()函数,在这里使用了为storage类型的_age作为函数modifyAge的参数,相当于创建了一个临时变量age(memory类型),将storage类型的变量_age赋值给memory类型的变量age,是值传递,所以在modifyAge函数中,age变量的值的变化并不会影响到_age变量的值所以再查看_age的值,还是为30

(2)

pragma solidity ^0.4.24;

contract Person {

string public _name;

constructor() public {

_name = "chenqin";

}

function f() public view{

modifyName(_name);

}

function modifyName(string name) public pure{

string memory name1 = name;

bytes(name1)[0] = 'L';

}

}

分析

在这里一开始deploy合约时,设置的_name为"chenqin"然后,调用f()函数,将storage类型的状态变量_name作为参数,赋值给函数modifyName函数的memory类型的name,为值传递之后,在modifyName函数中,还将memory类型的name赋值给memory类型的name1,为引用传递!改变一个另一个也跟着改变,最后,因为先是进行了值传递,name与_name之间已经互不影响了,所以不会跟着改变_name。(标记)处的代码并不会修改_name的值因此,不管如何以上函数,_name始终为chenqin

(3)

pragma solidity ^0.4.24;

contract Person {

string public _name;

string public changedName;

constructor() public {

_name = "chenqin";

}

function f() public{//不能声明为view,因为改变了状态变量

modifyName(_name);

}

function modifyName(string name) public{//不能声明为view,因为改变了状态变量

changedName = name;

bytes(name)[0] = 'L';

}

}

分析

调用f()函数,将storage类型的状态变量_name作为参数,赋值给函数modifyName(string) memory类型的name形参,为值传递然后,memory类型的name作为形参,赋值给storage类型的状态变量changedName,为值传递因此,(标记)的那行代码,name的值的改变不会导致changedName的值的改变,更不要说_name了调用f函数,最终的结果是:_name=chenqin,changeName=chenqin

storage作为参数,赋值给storage

pragma solidity ^0.4.24;

contract Person {

string public _name;

constructor() public {

_name = "chenqin";

}

function f() public{

modifyName(_name);

}

function modifyName(string storage name) internal {

string storage name1 = name;

bytes(name1)[0] = 'L';

}

}

PS:如果modifyName函数不声明为internal会报错:这是因为形参是默认为memory类型的,这里声明为storage,那么函数的类型就必须声明为internal或者private

分析

调用f()函数,首先会将为storage类型的_name变量,赋值给modifyName函数storage类型的name,为引用传递然后在modifyName函数中,将storage类型的name变量,赋值给storage类型的name1变量,为引用传递都为引用传递,所以最后name1值的变化会导致_name的值的变化调用f函数前:_name=chenqin。调用f函数后:_name=Lhenqin

引申:其实在这里如果将modifyName(string)函数改成如下,也是能够成功的,因为其实没必要进行两次引用传递

function modifyName(string storage name) internal {

bytes(name)[0] = 'L';

}

memory作为参数,赋值给memory

pragma solidity ^0.4.24;

contract Person {

function modifyName(string name) public pure returns(string){

string memory name1 = name;

bytes(name1)[0] = 'L';

return name;

}

}

分析

这里调用modifyName函数,将memory类型的name,赋值给memory类型的name1,为引用传递这时候改变name1的值,它的值也随之改变

memory作为参数,赋值给storage

pragma solidity ^0.4.24;

contract Person {

string public _name;

constructor() public {

_name = "chenqin";

}

function f(string name) public{

_name = name//(x)

name = "ikun"(y)

}

}

分析

调用f函数,将memory类型的name,赋值给storage类型的_name,为值传递(x)处_name的值会被修改成name,然后不再随name的改变而改变,即(y)处代码对_name无影响。f函数执行完的结果还是:_name=chenqin

查看原文