solidity智能合约-知识点
solidity智能合约入门基础
区块链的价值
信任
创建永久的、安全的、不可篡改的可追溯的记录
价值
独一无二的资产转移,不需要第三方的组织
可靠
分布式、稳定性
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用二进制表示为:000000113<<1:000001103>>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,3return 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->503465963245955021447394431766750955009431831077address 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;// 带参数的modifiermodifier 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";}
}