Solidity-内存、引用与持久化存储

1、 内存与区块链——storage与memory原理

在前面的学习当中,我们知道了,合约中的一个被​​public​​修饰的成员变量,会默认生成一个供外部调用的函数,而这个函数是存储在区块链自身的数据结果里的。对于函数内部的局部变量,仅存在与内存当中。

我们看如下例子:

pragma solidity ^0.4.16;


contract MemoryTest{


function add(uint num) view returns(uint){

num += 1;

return num;

}


function test() view returns(uint,uint){

uint i = 2;

uint j = add(i);

return(i,j);
}
}

编译执行以上代码后,可以得到以下结果:

通学智能合约系列(十九)--memory与storage_java

当我们改变输入的​​num​​​的值,尽管​​add​​​函数的返回值是变化的,但是我们​​test()​​​函数的返回值,始终是​​2​​​和​​3​​​.原因是什么呢?就是因为我们的​​i​​​和​​j​​​作为局部变量,在设置​​num​​​值给​​add()​​​函数的时候,仅仅是拷贝了一份副本数据给变量​​i​​​,而实际参数​​i​​​是确定被赋值为2的。所以​​j​​​的值也是恒定为​​3​​​的。这个地方大家可以类比​​java​​中的形参和实参。

具体的内存模型如下图:

通学智能合约系列(十九)--memory与storage_智能合约_02

关于我们的变量​​num​​​,​​i​​​,​​j​​​其实都是存储在​​memory​​​中的,当合约函数被执行完成之后,这些内存数据也就消失了。而对于我们的成员变量,也可能理解成我们的合约函数,是存储在​​storage​​当中的,这些数据会被永久保存。

上图,我们画的公有成员变量​​k​​,我们来尝试修改它吧。

uint public k  = 20;

function changeIt(){

add(k);
}

编译执行之后,我们发现​​k​​​值是不会发生变化的。这个相当于将区块链上的数据​​k​​​重新拷贝了一份到​​add()函数​​的

形参​​i​​​中去做运算,实际上​​k​​的值是不会变化的。

我们下面再来看一个例子:

pragma solidity ^0.4.16;


contract MemoryTest{

uint public num1 = 5;

uint public num2 = num1;

function test() view returns(uint){

uint i = num1;
uint j = i;

j++;
return i;
}

function test2() view returns(uint){
uint i = num1;
uint j = i;
i++;
return i;

}
}

经过编译执行后,我们发现,以上各个变量之间都是彼此独立,互不影响的。各自有自己的内存空间,修改是不会影响其他变量的。

以上我们介绍的都是我们的​​memory​​内存。

在写这篇文章的时候,我脑海中对自己提出了一个疑问,那就是智能合约到底存在哪里?

经过一番搜索,找到以下以下答案,供大家一起探讨,如果你有好的想法,可以留言发表呀~

2、 storage引用详解

在这一小节中,我们将重点介绍​​storage​​内存。

如果小伙伴们有接触过​​java​​​的内存分配模型,应该都知道​​java​​的基本类型是存储在栈中的,而对象的内容或者数组的内容是存储在堆中的。如下图。

通学智能合约系列(十九)--memory与storage_java_03

这里我们为什么要提到堆栈呢?主要还是因为这​​solidity​​​中的​​memory​​​和​​storage​​​就类同于我们​​java​​​的​​栈​​​和​​堆​​啊,有没有?

下面我们来看一段代码

pragma solidity ^0.4.16;


contract StorageTest{
// 这个状态变量存储在区块链的网络之上
uint[] arrx;
// 当我们传递这个可变长度数组的时候,会在内存中为它分配空间
function test(uint[] arry) view returns(uint[]){
// 将内存的arry拷贝给区块链上的arrx变量
arrx = arry;
// 当我们在函数体内部定义了一个可变长度的数组时,实际上,他默认的类型是storage类型。
uint[] z = arrx;

return z;
}

}

针对​​storage​​类型的操作

pragma solidity ^0.4.16;


contract StorageTest{

uint[] arrx;

function test(uint[] arry) view returns(uint){
arrx = arry;
// 当我们在函数体内部定义了一个可变长度的数组时,实际上,他默认的类型是storage类型,
他指向了区块链上的arrx,所以当我修改z的元素的时候,我们实际上再操作的是区块链上的arrx
uint[] z = arrx;
// 通过指针实际身上修改了区块链上的arrx的值
z[0] = 100;
//通过指针实际上修改了区块链上arrx的长度,说明z和zrrx其实是一样的,操作z的时候,会改变arrx的值。
z.length = 100;

return z[0];
}
// 返回arrx的第一个元素
function test2() returns(uint){
return arrx[0];
}
// 返回arrx的长度
function test3() returns(uint){
return arrx.length;
}

}

关于​​solidity​​的两种内存特性就介绍到这里。


通学技术 面向区块链编程 学通技术 构建可信任社会