[toc]
solidity智能合约入门基础
区块链的价值
信任
创建永久的、安全的、不可篡改的可追溯的记录
价值
独一无二的资产转移,不需要第三方的组织
可靠
分布式、稳定性
solidity 语言
代码结构
pragma solidity ^0.4.23;
contract HelloWorld{
string public name = "DNA";
function getName() public view returns(string){
return name;
}
function changeName(string _name) public{
name = _name;
}
}
基本solidity变量类型
- bool:true、false
- string:"sdfsdfsd"
- int:0,999,-999
- byte:0x12,0xab
整型变量
int:有符号,int8,int16,int24,int32...int256
uint:无符号,uint8,uint16,uint24,uint32...uint256
tips:在solidity变量声明时,int默认声明int256,uint默认声明uint256
bool逻辑操作
函数
函数类型
view: 读取区块链上的数据,但是不修改区块链上面的数据
pure:不修改也不读取区块链上面的数据
function pingfang(uint a,uint b) public pure returns(uint){
return a**b;//a**b,表示a的b次方
}
一笔事务的控制台信息
调用上面的changeName(string _name)
函数,控制台信息如下:
transaction cost:事物总共花费,包括execution cost
execution cost:计算花费
input:
位运算
&(与):全都是1才为1,不然全是0
|(或):全是0为0,有一个1就是1
^(异或):相等为0,不等为1
~(取反):0变1,1变0
十进制3用二进制表示为:00000011
3<<1:00000110
3>>1:00000001
byte类型
位是计算机中存储数据最小的单位,只能存储0或1
用位操作数据比较麻烦,我们把位封装为byte
- bytes1代表1个字节,bytes2代表2个字节,bytes32代表32个字节
- bytes32表示的大小等同于int256(int256中的256表示256位)
十进制3用16进制表示为0x03,一位16进制==4位二进制
固定长度字节数组的代码
pragma solidity ^0.4.20;
contract bytesTest{
bytes2 a = 0x6a5c;//定义固定长度字节数组
function changeData()public {
a[0]=0x00; //编译器会报错:TypeError:Expression has to be an lvalue
//意思是a[0]必须是可以修改的左值
}
function changeLength(){
a.length=a.length+1;//编译器会报错:TypeError:Expression has to be an lvalue
//意思是a.length不可以被修改
}
}
==总结:固定长度的字节数组的数据和长度都不能被更改==
动态长度字节数组的代码
pragma solidity ^0.4.20;
contract bytesTest{
bytes a=new bytes(2);
function initValue()public{
a[0]=0x6a;
a[1]=0x7b;
}
function getLength()public view returns(uint){
return a.length;
}
function getValue()public view returns(bytes){
return a;
}
function changeData()public returns(bytes){
a[0]=0x12;
return a;
}
function changeLength()public{
a.length+=1;
}
function pushBytes()public{
a.push(0x11);
}
}
总结:
- 动态长度字节数组的数据和长度==可以被更改==,增加长度在数据==末尾补零==,减少长度直接==截取数据==(从低位开始截取,右边低位,左边高位)。
- 动态字节数组的特有方法push(),直接在数据末尾添加数据
固定长度字节转换
contract fixToDynamic{
bytes6 name=0x6f56e5e7e4e3;
function toDynamic()public view returns(bytes){
bytes memory rtn = new bytes(name.length);
for (uint i = 0;i<name.length;i++){
rtn[i]=name[i];
}
return rtn;
}
}
tips:循环中索引的变量声明一定要是uint
字符串-特殊的动态字节数组
想要操作字符串需要把字符串转化为动态长度字节数组
字符串转动态字节数组,直接强制类型转换即可:bytes(str)
字符串==无法通过==以下方式获得字符串的长度,和指定索引下的数据
contract StringTest{
string public name = "DNA";
function getLength()public returns(uint){
return name.length; //无法通过此种方式获取字符串长度
}
function getData()public returns(bytes1){
return name[0]; //无法通过此种方式获取指定索引处的字符
}
}
把字符串转化为动态长度字节数组后继续操作
contract StringTest{
string public name = "DNA";
function getLength()public view returns(uint){
return bytes(name).length;
}
function getData()public returns(bytes1){
return bytes(name)[0];
}
}
在函数内声明动态长度字节数组时,需要加上memory
关键字:bytes memory newNane=new bytes(name.length)
关于memory
和storage
,我们后续详细讲解
动态长度字节数组转化为字符串
可以直接将动态长度字节数组强制类型转化为字符串string(dynamicArr)
pragma solidity ^0.4.20;
contract DynamicToString{
bytes a=new bytes(2);
function initValue()public{
a[0]=0x6a;
a[1]=0x6f;
}
function dynamicToStr()public view returns(string){
return string(a);
}
}
固定长度字节数组转化为字符串
==不能像动态长度字节数组一样,直接强制类型转化为字符串==。而需要把固定数组转为动态数组,再将动态数组转化为字符串。
pragma solidity ^0.4.20;
contract FixToString{
bytes2 name=0x6a6f;
function fixToStr(bytes32 _data)public pure returns(string){
uint count = 0;
for(uint i=0;i<_data.length;i++){
if(_data[i]!=0){
count++;
}
}
bytes memory rtn = new bytes(count);
for(uint j=0;j<rtn.length;j++){
rtn[j]=_data[j];
}
return string(rtn);
}
}
固定长度数组
contract FixArray{
uint[5] public grade=[1,2,3,4,5];
int[5] math;
bytes2[3] bytesTest;
function init()public{
bytesTest[0]=0x6af6;
}
function getArray()public view returns(bytes2[3]){
return bytesTest;
}
function init2()public{
grade[0]=100;
grade[1]=200;
}
function getArray2()public view returns(uint[5]){
return grade;
}
function getLength()public view returns(uint){
return grade.length;
}
}
注意:因为是固定长度数组,故不能够更改数组长度,也不能通过array.push()
增加数据
动态长度数组
pragma solidity ^0.4.20;
contract DynamicArray{
uint[] grade=[1,2,3,4,5];
function getArray()public view returns(uint[]){
return grade;
}
function getLength()public view returns(uint){
return grade.length;
}
//先调用changeLength1(),后调用changeLength2(),最后调用pushData()
function changeLength1()public {
grade.length=3; //grade[1,2,3,4,5]->grade[1,2,3]
}
function changeLength2()public {
grade.length=5; //grade[1,2,3]->grade[1,2,3,0,0]
}
function pushData()public{
grade.push(99);//grade[1,2,3,0,0]->grade[1,2,3,0,0,99]
}
}
二维数组
solidity中二维数组的定义和其他语言的不一样,比如golang中
arr:=[2][3]int{{1,1,1},{2,2,2}}
而solidity中,把二维的长度放在前面,一维的长度放在后面
uint[3][2] arr=[[1,2,3],[1,2,3]];
但solidity中二维数组长度的获取却和golang一样,arr.length
表示的一维的长度,而arr[0].length
表示的二维的长度
solidity获取二维数组元素的方式和规则与golang一样,arr[0][0]
表示arr数组中第一维的第一个元素
contract twoArray{
uint[3][2] arr=[[1,2,3],[1,2,3]];
function getArr()public view returns(uint[3][2]){
return arr;
}
function getLength1()public view returns(uint){
return arr.length;//返回2
}
function getLength2()public view returns(uint){
return arr[0].length;//返回3
}
function getLength3()public view returns(uint){
return arr[1].length;//返回3
}
function changeData()public returns(uint[3][2]){
arr[0][1]=8023;//uint256[3][2]: 1,8023,3,1,2,3
return arr;
}
}
}
注意:二维数组不能够修改长度,也不能像动态数组一样push()
动态二维数组
pragma solidity ^0.4.20;
contract dynamicTwoArray{
uint[][] arr=[[1,2,3],[4,5,6]];
// function getArr()public view returns(uint[][]){
// return arr;
// }
function getLength1()public view returns(uint){
return arr.length;
}
function getLength2()public view returns(uint){
return arr[0].length;
}
function changeLength()public {
arr.length=3;// 可以改变一维数组的长度,但是没有获取扩容后的数据
}
function changeLength2()public {
arr[0].length=4;//可以改变二维数组的长度,并且扩容的数据为类型默认值
}
function getData(uint a,uint b)public view returns(uint){
return arr[a][b];
}
function pushData()public{
arr.push([7,8,9,10]);//可以向动态一维数组后面push数据
}
function pushData2()public{
arr[0].push(56);//可以动态的像二维数组后面push数据
}
}
==注意:动态二维数组不能作为函数返回值==
数组字面量
contract ArrayLiteratrals{
function getArrayLiteratel1()public pure returns(uint[3]){
return [1,2,3];//会报错,提示不能用uint8的数组作为uint数组的返回值
}
function getArrayLiteratel1()public pure returns(uint[3]){
return [1,256,3];//会报错,提示不能用uint16的数组作为uint数组的返回值
}
function getArrayLiteratel()public pure returns(uint[3]){
return [1,2,uint(3)];//解决了上述问题
}
}
地址类型
类型大小:160位二进制,40位16进制
pragma solidity ^0.4.20;
contract addressTest{
// 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c->1154414090619811796818182302139415280051214250812
// 0x583031D1113aD414F02576BD6afaBfb302140225->503465963245955021447394431766750955009431831077
address public account1 = 0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c;
address public account2 = 0x583031D1113aD414F02576BD6afaBfb302140225;
function changeIt()public view returns(uint160){
return uint160(account1);//1154414090619811796818182302139415280051214250812
}
function changeIt2()public pure returns(address){
return address(1154414090619811796818182302139415280051214250812);//0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c
}
function check1()view public returns(bool){
return account1>account2;//true
}
}
转账操作
如何让外部账户的钱,转到合约账户当中?
pragma solidity ^0.4.20;
contract payableTest{
//外部账户调用 pay()并且设置value值的时候,可以将钱转到合约账户中
function pay()public payable{
}
function getBanlance()public view returns(uint){
return address(this).balance;
}
function getThis()public view returns(address){
return this;//本合约的地址:0x9240dDc345D7084cC775EB65F91f7194DbBb48d8
}
//获取外部账户余额
function getExternalBalance(address account)public view returns(uint){
return account.balance;
}
//给指定地址(account)转账
function transfer1()public payable{
address account=0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db;
account.transfer(msg.value);
}
//向当前合约账户转账
function transfer2() public payable{
address(this).transfer(msg.value);
}
//向指定账户转钱
function transfer3() public payable{
address(0x4b0897b0513fdc7c541b6d9d7e929c4e5364d2db).transfer(10*10**18);
}
function () public payable{
}
}
全局属性
pragma solidity ^0.4.20;
contract golobal{
// block.coinbase (address): :当前块的矿工的地址
// block.difficulty (uint):当前块的难度系数
// block.gaslimit (uint):当前块汽油限量
// block.number (uint):当前块编号
// block.blockhash (function(uint) returns (bytes32)):指定块的哈希值——最新的256个块的哈希值
// block.timestamp (uint):当前块的时间戳
// msg.data (bytes):完整的calldata
// msg.gas (uint):剩余的汽油
// msg.sender (address):消息的发送方(当前调用)
// msg.sig (bytes4):calldata的前四个字节(即函数标识符)
// msg.value (uint):所发送的消息中wei的数量
// now (uint):当前块时间戳(block.timestamp的别名)
// tx.gasprice (uint):交易的汽油价格
// tx.origin (address):交易发送方(完整的调用链)
function getGlobal1()public view returns(address){
return block.coinbase;
}
}
转账的三种方式
address.transfer()
contract transferDepp{
function transfer1(address _address)public payable{
_address.transfer(msg.value);
}
function transfer2(address _address)public payable{
_address.transfer(10*10**18);
}
function transfer3(address _address)public payable{
_address.transfer(10 ether);
}
}
address.send()
contract transferDepp{
//返回值true表示转账成功,否则失败
function transfer4(address _address)public payable returns(bool){
return _address.send(10 ether);
}
}
注意:send()和transfer()的区别是在于,函数调用者的付款金额如果小于合约中函数的转账金额,transfer会直接执行失败,全部回滚。而send会执行成功,直接扣除函数调用者的以太币,不转账给收款方,而是当累计付款金额达到标准后再一并转账给收款方。
address.call.value().gas()()
本方法不能避免重入攻击;transfer和send都可以避免重入攻击
function transfer5(address _address)public payable returns(bool){
return _address.call.value(10 ether)();
}
总结:
- transfer与send相似,都是转账操作
- transfer出错抛出异常,全都回滚
- send出错不抛出异常,返回true或false
- send无论出错与否,前后代码依旧执行,return后面的代码不执行
- tranfer相对于send更安全
mapping映射
定义方式:
mapping(类型 => 类型) 变量名
简单模拟网站注册:
pragma solidity ^0.4.20;
contract mappingTest{
uint id=0;
mapping(address=>uint) idmap;
mapping(uint=>string) namemap;
function regist(string name) public{
address account=msg.sender;
id++;
idmap[account]=id;
namemap[id]=name;
}
function getIDByAddress(address _address)public view returns(uint){
return idmap[_address];
}
function getNameByID(uint _id)public view returns(string){
return namemap[_id];
}
}
函数与函数重载
函数的定义
function 函数名(){private|public|external|internal}[pure|view|payable|const][returns(<type>)]
函数的重载
pragma solidity ^0.4.20;
contract mappingTest{
uint public num = 2;
function change() public{
num = 10;//思考问题:区块链上的数据不可更改,但为什么如此操作可以成功执行
}
//将bytes6转化为动态字节数组
function Todynamic(bytes6 name) pure public returns(bytes){
bytes memory newName = new bytes(name.length);
for(uint i = 0;i<name.length;i++){
newName[i] = name[i];
}
return newName;
}
//复用Todynamic()函数
bytes public myname;
function getlength(bytes6 name) public returns(uint){
myname = Todynamic(name);
return myname.length;
}
// 如下两个函数无法通过编译
// function fun(){
// }
// function fun(){
// }
// 如下四个函数无法通过编译
// function fun() returns(uint){
// }
// function fun() returns(bytes1){
// }
// function fun() returns(uint){
// }
// function fun(){
// }
// 如下两个函数可以通过编译
function fun(uint k) public pure{
}
function fun() public pure{
}
//如下两个函数可以通过编译
function fun2(uint a) public pure{
}
function fun2(bytes1 b) public pure{
}
//如下两个函数可以通过编译
function fun3(uint a) public {
num = 256;
}
function fun3(uint8 b) public{
num = 8;
}
function test() public{
//fun3(2); //不能通过编译
//fun3(uint8(2)); //不能通过编译
//fun3(uint256(2)); //通过编译
fun3(256); //通过编译
}
// 如下两个函数可以通过编译
function fun4(address a) public {
num = 256;
}
function fun4(uint160 b) public{
num = 8;
}
function test3() public{
fun4(address(0x14723a09acff6d2a60dcdf7aa4aff308fddc160c)); //通过编译
//fun4(0x14723a09acff6d2a60dcdf7aa4aff308fddc160c); //不能通过编译
//fun4(uint160(0x14723a09acff6d2a60dcdf7aa4aff308fddc160c)); //不能通过编译
}
function test4() public{
fun4(address(123));
}
}
重载的条件:
- 函数的返回值是没有办法区别函数的
- 参数的个数可以区别函数
- 参数的类型一定程度上可以区别函数,详情看上面代码示例
函数参数
pragma solidity ^0.4.20;
contract funcParam{
uint public num;
string public name;
function setparam(uint _num,string _name) public {
num = _num;
name = _name;
}
function Test() public {
setparam(99,"bob");
}
function Test2() public {
setparam({_num:99,_name:"bob"});
}
function Test3() public {
setparam({_name:"Alice",_num:999});
}
// 如下函数无法通过编译
// function Test4() public {
// setparam(100);
// }
}
tips:编译通过后,如果在deploy contracts上面传递的参数不全时,会给未传递的参数自动传递类型默认值
函数返回值
pragma solidity ^0.4.20;
contract returnValue{
function resValue() pure public returns(uint){
uint a = 10;
return a;
}
function recieveValue() pure public returns(uint){
uint b;
b = resValue();
return b;
}
function resValue2() pure public returns(uint num1){
num1 = 100;
}
function resValue3() pure public returns(uint num1){
num1 = 100;
return 99; // 返回99
}
function resValue4() pure public returns(uint num1){
uint b = 88; // 返回值是0
}
function mulvalue(uint a,uint b) pure public returns(uint,uint){
uint add = a+b;
uint mul = a*b;
return (add,mul);
}
function mulvalue2(uint a,uint b) pure public returns(uint add,uint mul){
add = a+b;
mul = a*b;
}
function reverse(uint a,uint b) returns(uint ,uint){
return (b,a);
}
uint public resA = 0;
uint public resB = 0;
function reverse2(uint a,uint b) {
(resA,resB) = reverse(a,b);
}
}
tips:多返回值要用“()”括起来
变量作用域
pragma solidity ^0.4.20;
contract scope{
uint public a = 100;
uint b = 200;
// uint public a = 999;
function scopeTest() pure public returns(uint){
uint a = 88;
a = 77;
return a; // 返回值是77
}
function scopeTest2(uint a) pure public returns(uint){
// uint a = 0; //编译失败
// for(uint a = 0;a<8;a++){ // 编译失败
// }
{
// uint a = 0; //编译失败
a = 99;
return a ;
}
}
}
值拷贝
pragma solidity ^0.4.23;
contract valueCopy{
uint public a = 100;
uint public b = a ;
function change() public {
b = 999;//此操作不会改变a的值
}
function change2() public pure returns(uint,uint){
uint a1 = 100;
uint b1 = a1;
b1 = 999;
return (a1,b1);//返回100,999
}
function change3(uint num) public returns(uint){
num++;
return num;
}
function test() returns(uint){
uint result = change3(a);
return result; //不会改变a的值,返回101
}
}
构造函数
构造函数名字要与合约名字完全一样(==已过时==),现在构造函数用constructor来定义
pragma solidity ^0.4.20;
contract ontractinit{
uint public a ;
// function ontractinit() public{
// a = 100;
// }
// function ontractinit(uint _a,uint _b) public{
// a = _a;
// }
address public owner;
// constructor(uint _a) public{
// a = _a;
// }
constructor() public {
owner = msg.sender;
}
}
modifier
modifier的定义
modifier的执行逻辑
modifier示例代码
pragma solidity ^0.4.23;
contract modifire{
address owner;
uint public a = 0;
constructor() public{
owner = msg.sender;
}
modifier OnlyOwner(){
require(msg.sender==owner);//需要函数调用者和owner相同才可以执行后面的函数逻辑
_;
}
function changeIt(uint _num) public OnlyOwner{
a = _num;
}
function getIt() view public OnlyOwner returns(address) {
return owner;
}
}
pragma solidity ^0.4.23;
contract modifiererParam{
uint public level = 9;
string public name;
uint public DNA;
// 带参数的modifier
modifier controlLevel(uint _needlevel){
require(level>_needlevel);
_;
}
function changeName(string _name) public controlLevel(2){
name = _name;
}
function changeDNA(uint _dna) public controlLevel(10){
DNA = _dna;
}
}
contract mulmodifiererDeep{
uint public a = 0;
modifier mod1{
a = 1;
_;
a = 2;
}
function test() public mod1{
a = 100; // 最终a的值为2
}
}
contract mulmodifiererDeep2{
uint public a = 0;
modifier mod1{
a = 1;
_;
a = 2;
}
modifier mod2{
a = 3;
_;
a = 4;
}
function test() public mod1 mod2{
a = 100; // a的值为2
}
}
contract mulmodifiererDeep3{
uint public a = 0;
modifier mod1{
a = 1;
_;// a = 100;
a = 2;
}
modifier mod2{
a = 3;
_;
a = 4;
}
function test() public mod2 mod1{
a = 100; // a的值为4
}
}
const与继承
5.0版本后,constant关键字就被废除了
继承通过关键字is实现
pragma solidity 0.4.23;
contract constantTest{
uint public constant num = 100;
int public constant num2 = 100;
bytes32 public constant num3 = 0x54;
string public constant num4 = "jonson";
//bytes
// bytes32[] public constant num5;
function test() public pure returns(uint){
return num;
}
function change() public {
// num = 99;
}
}
contract father{
uint public money =10000;
function dahan() public returns(string){
return "dahan";
}
}
contract son is father{
uint public girlfriend;
function change() public{
money = 99;
}
}
可见性
function 函数名(){private|public|external|internal}[pure|view|payable|const][returns(<type>)]
public:任何人都可以调用该函数,包括Dapp的使用者。
private:只有合约本身可以调用该函数(在另一个函数中)。
internal:只有这份合同以及由此产生的所有合同才能称之为合同。
external:只有外部可以调用该函数,而合约内部不能调用。
状态变量的可见性
- 没有external属性
- public、internal 或者不加任何修饰符,都可以被继承
- private不能被继承
函数的可见性
- public:可以被继承,子类含有父类方法,可以被子类任意调用
pragma solidity 0.4.23;
contract father{
uint public money =10000;
function dahan()public pure returns(string){
return "dahan";
}
}
contract son is father {
function getMoney() public view returns(uint){
return money;
}
function test()public pure returns(string){
return dahan();
}
}
- external:可以被继承,子类含有父类方法,但子类调用必须通过
this.继承的方法();
来定用
pragma solidity 0.4.23;
contract father{
uint public money =10000;
function dahan()external pure returns(string){
return "dahan";
}
}
contract son is father {
function getMoney() public view returns(uint){
return money;
}
function test()public view returns(string){
return this.dahan();
}
}
- internal:可以被继承,但子类不含有父类方法(不能内部直接调用),但是可以调用父类的方法(可以通过外部调用,如下面的
test()
方法)
pragma solidity 0.4.23;
contract father{
uint public money =10000;
function dahan()internal pure returns(string){
return "dahan";
}
}
contract son is father {
function getMoney() public view returns(uint){
return money;
}
function test()public pure returns(string){
return dahan();
}
}
- private:不能被任何人所继承
可以在一份合约中实例化另一份合约
pragma solidity ^0.4.23;
contract father{
uint money = 10000;
function dahan() external pure returns(string){
return "dahan";
}
}
contract son is father{
function getMoney() public view returns(uint){
return money;
}
function test() public view returns(string){
return this.dahan();
}
}
contract testExternal{
father f = new father();
function test() public view returns(string){
return f.dahan();
}
}
getter函数
给变量添加public修饰符,如果自己不手写一个get函数的话,系统会自动生成一个get函数(下面的num()
),如果自己手写了,则系统不再继续生成
contract getter{
uint public num = 100;
function num() external pure returns(uint){
return 200;
}
}
因为系统自动生成的get函数是external的,所以调用的时候必须通过this.num()
调用
contract getter{
uint public num = 100;
function test(){
this.num();
}
mapping的getter函数
pragma solidity ^0.4.23;
contract getter{
mapping(uint =>string) public map;
//系统自动生成的getter函数如下
function map(uint key) external view returns(string){
return map[key];
}
function change() public{
map[2] = "jonson";
}
}
因为系统自动生成的get函数是external的,所以调用的时候必须通过this.map()
调用
function test() returns(string){
return this.map(2);
}
contract getter{
mapping(uint=>mapping(uint=>uint)) public grademap;
function changegrade() public{
grademap[1][21] = 99;
grademap[2][21] = 56;
}
}
多继承
多继承中相同变量以is
后面的父类为标准(函数也如此)
- son的money为8888
contract father{
uint public money=9999;
}
contract mother{
uint public money=8888;
}
contract son is father,mother{
//son的money为8888
}
- son的money为9999
contract father{
uint public money=9999;
}
contract mother{
uint public money=8888;
}
contract son is mother,father{
//son的money为9999
}
contract father{
uint public money=9999;
function dahan()public pure returns(string){
return "father";
}
}
contract mother{
uint public money=8888;
function dahan()public pure returns(string){
return "mother";
}
}
contract son is father,mother{
function dahan()public pure returns(string){
return father.dahan();//返回结果是"father"
}
}
总结:
- 支持函数重写
- 变量的使用遵循“就近原则”,也就是说,子类定义了和父类同名的变量时,子类中使用的变量就是子类中定义的
- 不能通过super.money访问父类变量
- 可以通过
super.dahan()
访问父类方法,但不能super.super.dahan()
访问父类的父类的方法 - 可以通过
父类合约的名字.方法()
来访问父类的方法
EVM数据存储
storage与memory
storage:状态变量(全局变量)会存储在这里。永久性存储数据(因为会把数据写入区块链当中),
stack:函数中的本地变量(局部变量)默认会存储在这里,暂时性存储数据。
memory:暂时性存储数据,实参(函数中实际传递的参数)默认会存储在这里。
tips:storage和memory都需要消耗gas,但是storage更贵。
代码1:
pragma solidity ^0.4.23;
contract storageAndMemory{
uint a = 5;//storage变量
function changeIt() public{
a=1000;//stack变量
}
function add(uint num)public pure returns(uint){
num+=1; //因为num是作为参数传进来的,所以是memory变量
return num;
}
function test()public pure returns(uint,uint){
uint i =2; // stack变量
uint j=add(i);// stack变量
return (i,j);
}
}
代码2:
==动态数组在函数内部定义时,默认声明为storage
==
pragma solidity ^0.4.23;
contract storageStart{
uint[] public arrx;
function test(uint[] arry)public{
arrx=arry;
uint[] storage Z=arrx;//变量Z存储在stack中,但实际引用的是storage中的数据
Z[0]=100;
Z.length=10;
}
function getLength()public view returns(uint){
return arrx.length;
}
}
结构体
结构体在函数内部实例化的时候,==实例会存储在memory当中==,而==结构体声明的引用会被默认声明为storage,所以需要将引用显示声明为memory==
contract StructTest{
struct student{
uint grade;
string name;
}
function init()public pure returns(uint,string){
student memory s= student(100,"jackson");
// student memory s1= student({name:"Tommy",grade:99}); 也可以这种方式定义
return (s.grade,s.name);
}
}
注意:==结构体内部不能嵌套同一结构体==,但==可以嵌套自身的动态数组==。
contract StructTest{
struct student{
uint grade;
string name;
student s;//编译失败
}
}
注意:结构体可以嵌套自身的动态数组
contract StructTest{
struct student{
uint grade;
string name;
student[] s;//通过编译
}
}
注意:结构体内部可以嵌套另一个结构体
contract StructTest{
struct student{
uint grade;
string name;
}
struct student2{
uint grade;
string name;
student s;//通过编译
}
}
注意:结构体可以嵌套mapping
contract StructTest{
struct student{
uint grade;
string name;
mapping(uint=>student) studentMapping;
}
}
struct中的mapping
pragma solidity ^0.4.23;
contract StructDeep{
struct student{
uint grade;
string name;
mapping(uint=>string) map;// 在定义的时候不用初始化
}
student stu;
function init()public view returns(string){
student memory s=student(100,"jackson");
// s.map[2]="Allen";//memory当中的结构体不能操作内部的mapping对象
stu=s;//将memory当中s存储的student数据复制到storage中,并由stu所指向
stu.map[2]="Allen";
student storage tempStu = stu;//storage的 tempStu引用stu的数据,不会复制产生新数据
tempStu.map[2]="Alice";
return (stu.map[2]);
}
}
结构体作为函数参数,函数必须是internal类型
function structParam(student s) internal{
}
在函数内部声明storage变量,并引用状态变量,直接引用storage变量,不会重新复制一份
pragma solidity ^0.4.23;
contract StructDeep{
struct student{
uint grade;
string name;
mapping(uint=>string) map;// 在定义的时候不用初始化
}
student stu;
function structParam(student s) internal{
student memory guy=s;
}
function structParam2(student storage s) internal{
student storage guy=s;
guy.grade=1000;
}
function call() public returns(uint){
structParam2(stu);
return stu.grade;
}
}
contract memoryTomemory{
struct student{
uint grade;
string name;
}
student s1=student(99,"sdf");
student s2 =s1;//会在storage中复制一份完全相同的数据
}
memory转storage
pragma solidity ^0.4.23;
contract MemoryTostorage{
struct student{
uint grade;
string name;
}
student stu;
function test(student memory s) internal{
stu = s;
s.name = "alice";
}
function call() public returns(uint,string){
student memory guy = student(100,"jackson");
test(guy);//guy作为参数,会进行值拷贝,在memory中复制一份完全相同的数据传进去
return (stu.grade,stu.name);
}
}
storage转memory
pragma solidity ^0.4.23;
contract storageToMemory{
struct student{
uint grade;
string name;
}
student stu = student(100,"jackson");
function test(student storage s) internal{
student memory guy = s;//会在memory当中复制一份和相同的一份数据
guy.grade = 50;
}
function call() public returns(uint,string){
test(stu);
return (stu.grade,stu.name);
}
}
memory转memory
pragma solidity ^0.4.23;
contract memoryTomemory{
struct student{
uint grade;
string name;
}
function test(student memory b) internal{
student memory c = b;
c.name = "jonson";
}
function call() returns(string){
student memory a = student(100,"olaya");
test(a);
return a.name;//返回值是joson
}
}
枚举
pragma solidity ^0.4.23;
contract enumTest{
enum girl{fengjie,binbin,yuanyuan}//不能加分号
girl dategirl =girl.fengjie;
function getEnum() view returns(girl){
return dategirl;
}
function oneNightDate() returns(string){
require(dategirl == girl.fengjie);
dategirl = girl.binbin;
return "date fengjie";
}
function seconedNightDate() returns(string){
require(dategirl == girl.binbin);
dategirl = girl.yuanyuan;
return "date binbin";
}
}