当前位置: 首页 > news >正文

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);}}

总结:

  1. 动态长度字节数组的数据和长度==可以被更改==,增加长度在数据==末尾补零==,减少长度直接==截取数据==(从低位开始截取,右边低位,左边高位)。
  2. 动态字节数组的特有方法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)

关于memorystorage,我们后续详细讲解

动态长度字节数组转化为字符串

可以直接将动态长度字节数组强制类型转化为字符串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)();}

总结:

  1. transfer与send相似,都是转账操作
  2. transfer出错抛出异常,全都回滚
  3. send出错不抛出异常,返回true或false
  4. send无论出错与否,前后代码依旧执行,return后面的代码不执行
  5. 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));}}

重载的条件:

  1. 函数的返回值是没有办法区别函数的
  2. 参数的个数可以区别函数
  3. 参数的类型一定程度上可以区别函数,详情看上面代码示例

函数参数

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:只有外部可以调用该函数,而合约内部不能调用。

状态变量的可见性

  1. 没有external属性
  2. public、internal 或者不加任何修饰符,都可以被继承
  3. private不能被继承

函数的可见性

  1. 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();}
}
  1. 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();}
}
  1. 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();}
}
  1. 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后面的父类为标准(函数也如此)

  1. son的money为8888

contract father{uint public money=9999;
}
contract mother{uint public money=8888;
}
contract son is father,mother{//son的money为8888
}
  1. 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"}
}

总结:

  1. 支持函数重写
  2. 变量的使用遵循“就近原则”,也就是说,子类定义了和父类同名的变量时,子类中使用的变量就是子类中定义的
  3. 不能通过super.money访问父类变量
  4. 可以通过super.dahan()访问父类方法,但不能super.super.dahan()访问父类的父类的方法
  5. 可以通过父类合约的名字.方法()来访问父类的方法

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;}
}

uploading.4e448015.gif正在上传…重新上传取消

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";}
}

相关文章:

  • 开源音视频转文字工具:基于 Vosk 和 Whisper 的多语言语音识别项目
  • B/S架构和C/S架构的介绍与分析
  • 如何在LVGL之外的线程更新UI内容
  • 从纸质契约到智能契约:AI如何改写信任规则与商业效率?​——从智能合约到监管科技,一场颠覆传统商业逻辑的技术革命
  • Unreal 从入门到精通之SceneCaptureComponent2D实现UI层3D物体360°预览
  • 学习VS2022离线安装包的下载方法
  • STC-ISP烧录过程中一直显示“正在检测单片机”的解决办法
  • WebSphere Application Server(WAS)8.5.5教程第五讲
  • 解释加密中的加盐操作
  • 理解PostgreSQL查询执行计划(三)--复杂操作篇
  • C++17之std::launder函数
  • 【回溯法】0-1背包问题 C/C++(附代码)
  • nmcli connection reload
  • React集成百度【JSAPI Three】教程(002):设置不同的环境效果
  • OpenTelemetry 从入门到精通
  • 【MySQL】基础操作
  • 【Linux】进程控制(进程创建、进程终止、进程等待、进程替换)
  • Vue.js---立即执行的watch与回调执行时机
  • 扫描项目依赖漏洞
  • 网络学习-epoll(四)
  • 音著协宣布胜诉,虎牙公司终审被判侵权
  • 戛纳参赛片《爱丁顿》评论两极,导演:在这个世道不奇怪
  • 巴基斯坦外长访华是否与印巴局势有关?外交部:此访体现巴方高度重视中巴关系
  • 肖钢:一季度证券业金融科技投资强度在金融各子行业中居首
  • 从《缶翁的世界》开始,看吴昌硕等湖州籍书画家对海派的影响
  • 墨西哥海军帆船纽约撞桥事故已致2人死亡19人受伤