以太坊私有链以及solidity学习笔记

本文详细介绍了如何在以太坊平台上使用Solidity编写智能合约,包括合约部署、运行、查看状态变量、数据类型、函数修饰符、地址类型、数组操作等内容。讲解了view、pure修饰符的区别,以及如何进行转账操作。还探讨了 Solidity 中的地址、映射、结构体、枚举等高级特性,适合初学者和进阶开发者学习。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一,运行智能合约推荐两种途径

1,自己搭建私有链,推荐我写的另一篇文章,从头到尾教你怎么搭建私有链,并运行合约等一系列操作,喜欢的话帮忙点赞收藏一下。。。

https://blog.youkuaiyun.com/mlynb/article/details/117410575

2,在一种方法就是使用网站,推荐两个挺好用的

http://remix.hubwiz.com/
http://remix.ethereum.org/

如果你觉得看视频比较好的话推荐一个b站up主的视频

https://www.bilibili.com/video/BV1St411a7Pk?p=2

在这里插入图片描述

二,solidity初级篇

1.pragma+view+pure

pragma solidity ^0.4.0;
//^0.4.0代表向上版本兼容,编译器版本最高到0.5.0但不包括0.5.0
contract Helloworld{
    string Myname="tyl";
    //view 修饰函数只能读取状态变量,不消耗gas
    //这里的函数格式也跟c和java不一样,需要一个function在前面,然后函数名加(),而public这种的修饰词放在函数名后边,如果有返回值需要写returns,注意不是return,这里返回一个string,要加memory否则编译失败报这样的错:“TypeError: Data location must be "memory" or "calldata" for return parameter in function, but none was given. ”
    function getName() public view returns(string memory){
        return Myname;
    }
    //这里也要加memory
    function changeName(string memory  _newName) public{
        Myname=_newName;
    }
    //pure 修饰函数不能改也不能读状态变量,不消耗gas
    function pureTest(string memory _newName) public pure returns(string memory){
        return _newName;
    }
}

注意:Solidity
旧版本只有constant,新版本将constant拆成了view和pure。view的作用和constant一模一样,可以读取状态变量但是不能改;pure则更为严格,不读取更不修改区块上的变量,使用本机的CPU资源计算我们的函数。所以不消耗任何的资源这是很容易的理解的。要求的时不能读取,否则会报错,但是现在好像还没有严格实施,在remix上运行是可以的。

2.数据类型:bool、int、uint 、bytes、bytes1-bytes32

pragma solidity ^0.8.4;
contract dataClass{
   //布尔类型 bool 数据操作:逻辑与、逻辑或、逻辑非 ||、 && 、! 
   int num1=100;
   int num2=200;
   function boolcheck() public view returns(bool){
       return num1==num2;
   }//返回false
   function boolcheck1() public view returns(bool){
       return num1!=num2;
   } //返回true


   //整型 int=int256 uint=uint256 数据操作:加减乘除求余、求平方 +-*/%、**
    function intcheck(uint a,uint b) public pure returns(uint){
        return a**b;
    } //计算a的b次方

  //位运算:按位与、按位或、按位取反、按位异或、左移、右移 &、|、~、^、<<、>>  
    uint8 a=3; uint8 b=4;
    function weicheck() public view returns(uint8){
        return a|b;
    }//按位或,结果为7

   //整型字面量:在solidity里面运算是计算出结果再赋值
    function IntergerTest() public pure returns(uint){
        uint num=1/2*1000;
        return num;
    }//返回500

    //字节(数组)类型:bytes1(byte)、bytes2...bytes32,长度固定且内部数据不可修改
    //属性:length 可以进行比较,位运算
    bytes9 name=0xe69d8ee79fa5e681a9;
    function byteTest() public pure returns(uint){
        bytes1 num=0x7a;
        return num.length;
    }//返回1

     function getIndex(uint index) public view returns(bytes32){
        return name[index];
    }//按照index获得字节数组的值

}

动态字节数组:bytes num=new bytes();长度、内部数据均可修改,push方法和修改长度,均是在数组末尾变化

pragma solidity ^0.5.15;
contract DynamicByte{
   bytes  public  num=new bytes(2);//创建动态字节数组

    function InitNum() public{
        num[0]=0x12;
        num[1]=0x34;
    }//初始化数组

    function getlength() public view returns(uint){
        return num.length;
    }//获取数组长度
	//0.6.0以上的版本都不能使用length修改数组长度
    function setlength() public{
        num.length=5;
    }//修改数组长度

    function pushTest() public{
        num.push(0x56);
    }//push方法在数组末尾追加数据

}

3.字符串类型

pragma solidity ^0.5.15;
contract StringTest{
    string name='tyl';//0x74796c
    string name1="李知恩";//0xe69d8ee79fa5e681a9

    function getlength()public view returns(uint){
        return bytes(name).length;
    }//获得字符串的字节长度

    function getstrValue()public view returns(string memory){
        return name;
    }//获得字符串的str值

    function getValue()public view returns(bytes memory){
        return bytes(name);
    }//获得字符串的bytes值

    function getValue1()public view returns(bytes memory){
        return bytes(name1);
    }//获得字符串的bytes值


    function getfirst()public view returns(byte){
        return bytes(name)[0];
    }//获得字符串首个字节存储的值

    function setstrValue()public{
         bytes(name)[0]='T';
         bytes(name)[1]='Y';
         bytes(name)[2]='L';
    }//按字节修改字符串的值
}

字符串变量无法直接获得长度和修改字符串的值,需要进行bytes(字符串)强制转换后才能操作前面的操作

4,固定长度字节数组之间的转换

pragma solidity ^0.5.15;
contract StrTest{
    bytes9 name= 0xe69d8ee79fa5e681a9;

    function change1() public view returns(bytes1){
        return bytes1(name);
    }//0xe6

    function change2() public view returns(bytes10){
        return bytes10(name);
    }//0xe69d8ee79fa5e681a900
}

固定长度字符节数组长的变短,截取前面部分;短的变长的,在后面添0。

5,固定长度字节数组转换为动态长度字节数组

pragma solidity ^0.5.15;
contract StrTest1{
    bytes9 name=0xe69d8ee79fa5e681a9;

    function fixByte2dynamicByte() public view returns(bytes memory){
        bytes memory newByte=new bytes(name.length);
        for(uint i=0;i<name.length;i++){//细节问题:for循环里要求是uint变量
            newByte[i]=name[i];
        }
        return newByte;
    }
}

6,bytes to string

pragma solidity ^0.5.15;
contract StrTest1{
    bytes9 name=0xe69d8ee79fa5e681a9;
    function fixByte2dynamicByte() public view returns(bytes memory){
        bytes memory newByte=new bytes(name.length);
        for(uint i=0;i<name.length;i++){
            newByte[i]=name[i];
        }
        return newByte;
    }
    function bytes2string() public view returns(string memory){
        bytes memory IU=fixByte2dynamicByte();
        return string(IU);
    }//返回"李知恩"
}

小结:bytes与string可互相转换。string(字节数组)、bytes(字符串)

7,小结:bytes与string可互相转换。string(字节数组)、bytes(字符串)

pragma solidity ^0.5.15;
contract StrTest2{
   function bytes32Tostring(bytes32 _newname)  public pure returns(string memory){
       uint count=0;

       for(uint i=0;i<_newname.length;i++){
           bytes1 char=_newname[i];
           if(char!=0){
               count++;
           }
       }//找到字节数组中有用数据个数

       bytes memory newname=new bytes(count);

       for(uint j=0;j<count;j++){
           newname[j]=_newname[j];
       }//固定字节数组转换为动态字节数组

       return string(newname);//最终实现固定字节数组转换为string
   }
}

8,固定数组

pragma solidity ^0.5.15;
contract ArrayTest{
    //固定数组初始化
    uint[5] arr=[1,2,3,4,5];
    //获取数组元素并修改
    function Init() public{
        arr[0]=100;
        arr[1]=200;
    }
    //获取数组元素内容
    function getArrayContent() public view returns(uint[5] memory){
        return arr;
    }
    //对数组元素求和
    function getGrade() public view returns(uint){
        uint grade=0;
        for(uint i=0;i<arr.length;i++){
            grade+=arr[i];
        }

        return grade;
    }

}

固定数组没有push方法,也无法修改数组的length

9,可变数组

contract ArrayTest1{
    // 可变数组初始化
    uint[]  grade=[1,2,3,4,5];
    //获取数组元素内容
    function getContent() public view returns(uint[]) {
        return grade;
    }
    //获取可变数据长度
    function getLength() public view returns(uint){
        return grade.length;
    }
    //获取可变数组元素并修改
    function changeContent() public{
        grade[0]=100;
        grade[1]=200;
    }
    //对数组元素求和
    function add() public view returns(uint){
        uint sum=0;
        for(uint i=0;i<grade.length;i++){
            sum+=grade[i];
        }
        return sum;
    }
    //改变可变数组长度(缩短,只保留前面的数组元素)
    function changLength() public{
        grade.length=2;
    }
    //改变可变数组长度(增长,在数组后面添0)
    function changLength1() public{
        grade.length=10;
    }
    //push方法在数组后面追加
    function pushContent() public{
        grade.push(6);
    }

}

10,二维数组

定义静态二维数组时,行数和列数和其他语言不同,与其他语言刚好相反:数据类型[列数][行数]=[…….]。但在数组操作时,依旧是一样。静态二维数组同样不能改变数组长度

pragma solidity ^0.5.15;
contract ArrayTest2{
    //静态数组定义行数和列数刚好相反!!!
    uint[2][3]  grade=[[1,2],[3,4],[5,6]];
    //获取数组内容
    function getContent() public view returns(uint[2][3] memory){
        return grade;
    }

    function getRowLength() public view returns(uint){
        return grade.length;
    }//3行

    function getColumnsLength() public view returns(uint){
        return grade[0].length;
    }//2列

    function add() public view returns(uint){
        uint sum=0;
        for(uint i=0;i<grade.length;i++){//行
            for(uint j=0;j<grade[0].length;j++){//列
                sum+=grade[i][j];//取数组元素依旧和之前的语言一样
            }
        }
        return sum;
    } 
}

动态二维数组,可修改数组长度,暂时还无法支持动态二维数组作为返回值,获得数组内容

pragma solidity ^0.5.15;
contract ArrayTest3{
    //定义动态二维数组
    uint[][]  grade=[[1,2],[3,4],[5,6]];


    function getRowLength() public view returns(uint){
        return grade.length;
    }//3

    function getColumnsLength() public view returns(uint){
        return grade[0].length;
    }//2

    //改变行数
    function setRowLength() public{
        grade.length=4;
    }
    //改变列数,可针对任一行修改列数。
    function setColumnsLength() public {
         grade[0].length=4;
    }

    function add() public view returns(uint){
        uint sum=0;
        for(uint i=0;i<grade.length;i++){
            for(uint j=0;j<grade[i].length;j++){//注意和静态数组差别,每行的列数不一定相同
                sum+=grade[i][j];
            }
        }
        return sum;
    }

}

11,数组字面量

一般用于输入需求,返回数组字面量时,返回参数列表以数组字面量最小类型为准。

pragma solidity ^0.5.15;
contract ArrayTest3{

   function getArray1() public pure returns(uint8[4] memory){
       return [1,2,3,4];
   }//最小类型为uint8

   function getArray2() public pure returns(uint16[4] memory){
       return [256,2,3,4];
   }//最小类型为uint16

   function getArray3() public pure returns(uint[4] memory) {
       return [uint(1),2,3,4];
   }//可以强制将字面量转换为来适应返回参数列表类型

   function add(uint[4] memory grade) public pure returns(uint){
       uint sum=0;
       for(uint i=0;i<grade.length;i++){
           sum+=grade[i];
       }
       return sum;
   } //数组字面量用于输入

}

三,Solidity 进阶篇

1,地址 address

address 在存储上和uint160一样,且二者可以互相转换,地址之间也可以进行比较大小

pragma solidity ^0.5.15;
contract arrayTest{

  address public account=0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c;

  function changeIt() public view returns(uint160){
      return uint160(account);
  }//地址转换为uint160:1154414090619811796818182302139415280051214250812

  function Itchange() public view returns(address){
      return address(changeIt());
  }//uint160转换为地址:0xCA35b7d915458EF540aDe6068dFe2F44E8fa733c

}

地址分为外部账户地址和合约账户地址,合约是由外部账户发布的合约,每个合约都有自己的地址。和外部账户一样,合约账户也可以有钱(以太币)

pragma solidity ^0.5.15;
contract arrayTest1{
    //payable关键字,允许转账操作(这里是外部账户转账给合约账户)
    function pay() public payable{

    } 
    //返回合约账户地址
    function getAddress() public view returns(address){
        return msg.sender;
    }//0x038f160ad632409bfb18582241d9fd88c1a072ba

    //this关键字代表合约账户,账户都有balance(钱),这里“钱”单位默认是wei,返回合约账户的钱
    function getBalance() public view returns(uint){
        return address(this).balance;
    }

    //可以获得任意账户的钱
    function getBalance_by_addr(address account) public view returns(uint){
        return account.balance;
    }


}

2.转账操作

外部账户之间进行转账: account.transfer(msg.value);
外部账户向合约账户转账:address(this).transfer(msg.value);+回滚函数
其中msg.value是全局变量,代表转账的数量

pragma solidity ^0.5.0;
contract arrayTest2{
   //运行该合约的账户向外部account账户进行转账
   function transfer() public  payable{
       address payable account=0x14723A09ACff6D2A60DcdF7aA4AFf308FDDC160C;
       
       account.transfer(msg.value);
       
   }
   //按照地址获得账户余额
   function getBlance_by_addr(address account) public view returns(uint){
       return account.balance;
   }
   //外部账户向改合约账户进行转账,必须要有回滚函数
   function transfer1() public payable{
       address(this).transfer(msg.value);
   }
   //必须要有的回滚函数!!!
  /* function() public external  payable{

   }*/}

3.全局变量

account.transfer(msg.value)中msg.value可以直接用相应的值替换,e.g. account.transfer(10 ether);

4.send方法与transfer方法

function sendMoney() public payable  returns(bool){
       address account=0x14723a09acff6d2a60dcdf7aa4aff308fddc160c;
       return account.send(10 ether);
   }//send方法比较底层,如果不传入相应的转账金额,不会报错,只会返回false

  function transfer() public payable{
       address account=0x14723a09acff6d2a60dcdf7aa4aff308fddc160c;
       account.transfer(msg.value);
   }//transfer方法,如果不传入相应的转账金额,会进行报错提示

5.mapping类型

pragma solidity ^0.5.0;
contract mappingTest{
    //定义两个mapping类型的变量
    mapping(address=>uint) public idmapping;
    mapping(uint=>string) public namemapping;
    //用于代表注册的人数
    uint sum=0;
    //注册,主要是合约调用者的地址=>id;id=>姓名两组映射关系
    function register(string memory name) public{
        address account=msg.sender;
        sum++;
        idmapping[account]=sum;
        namemapping[sum]=name;
    }
    //根据地址来获得id
    function getIdbyAddr(address account) public view returns(uint){
        return idmapping[account];
    }
    //根据id来获得姓名
    function getNamebyId(uint id) public view returns(string memory){
        return namemapping[id];
    }
}

6. 函数重载
函数一般类型:

function 函数名(参数列表){private|internal|external|public}[pure|constant|view|payable][returns(返回值类型)]

1、函数名字相同
2、函数的参数列表不同(类型、数量)
3、不考虑函数的返回值类型

函数重载会遇到一个函数匹配的问题,例如add(1)与函数add(uint8)和add(uint)两个都匹配,发生冲突故报错!
其次参数列表类型是uint160和address时,无论输入的参数是什么类型,都会发生冲突,因为这两个本身机器是无法区分的。

7.关于函数命名参数说明

pragma solidity ^0.5.0;
contract Test{

    string public name;
    uint public id;
    function set(string memory _name,uint _id) public{
        name=_name;
        id=_id;
    }

    function test1() public{
        set("李知恩",123);
    }
    //通过这种给参数直接赋值的方式可以不考虑函数列表的先后顺序
    function test2() public{
        set({_name:"李知恩",_id:123});
    }

}

8.关于函数返回值说明

pragma solidity ^0.5.0;
contract Test{
   //函数返回值可以有名称
    function test1() public pure returns(uint mul){
        return 123;
    }
    //函数返回值可以不用return返回,直接给返回值赋值
    function test2()  public pure returns(uint mul){
        mul=123;
    }
    //函数返回值最终以return为主
    function test3()  public pure returns(uint mul){
        mul=123;
        uint a=100;
        return a;
    }
    //函数可有多个返回值
    function test4() public pure  returns(uint a, uint b){
        return(10,20);
    }

}

9.生命周期与作用域

全局变量名称在局部变量里可以出现重名的,此时修改局部变量不会影响全局变量。同时在函数里面,也不能出现重复定义的变量!

pragma solidity ^0.5.0;
contract test{
    //全局变量
    uint public a=100;

    function test0() public view returns(uint){
        return a;
    }//返回100

    function test1() public pure returns(uint){
        uint a=200;
        return a;
    }//重新定义局部变量a,返回200,但全局变量a依旧是100

    function test2(uint a) public pure returns(uint){
       // uint a=300;无法重复定义a,在参数列表内出现了重复
       a=300;
       return a;
    }//返回300
}

10.函数修饰符

pragma solidity ^0.5.0;
//public修饰符,可以在合约内部,继承合约内部,外部调用
contract father{
    //public修饰函数
    function dahan() public pure returns(string memory){
        return "dahan";
    }
    //合约内部调用
    function publictest() public pure returns(string memory){
        return dahan();
    }

}
contract son is father{
   //public修饰 继承合约可继承还可直接调用
   function test() public pure returns(string memory){
       return dahan();
   }

}
//=========================================================

//private修饰符,函数只能被本合约内部调用
contract father1{
     function dahan() private pure returns(string memory){
        return "dahan";
    }
    //合约内部调用
    function privatetest() public pure returns(string memory){
        return dahan();
    }
}
contract son1 is father1{
   //private修饰符,函数不能被继承合约继承和调用   
  /*  function test() public pure returns(string){
       return dahan();
    }*/

}
//==========================================================

//internal修饰符,函数只能被合约内部调用和继承合约内部调用
contract father2{
     function dahan() internal pure returns(string memory){
        return "dahan";
    }
    //合约内部调用
    function internaltest() public pure returns(string memory){
        return dahan();
    }
}
contract son2 is father2{
    //继承合约内部调用 
    function test() public pure returns(string memory){
       return dahan();
    }

}
//==========================================================

//external修饰符,只能在外部调用,或者在合约内部和继承合约内部间接调用(实际也是外部调用)
contract father3{
     function dahan() external pure returns(string memory){
        return "dahan";
    }
    //无法直接在合约内部调用
 /*   function externaltest() public pure returns(string){
        return dahan();
    }*/

    //间接调用
    function externaltest() public view returns(string memory){
        return this.dahan();
    }
}
contract son3 is father3{
     //无法直接在继承合约内部调用
  /*  function test() public pure returns(string){
       return dahan();
    }*/

    //间接调用
    function test() public view returns(string memory){
        return this.dahan();
    }

}
//通过另外不相关的合约,new一个合约对象,也可以间接调用
contract externaltest{
    father3 f=new father3();
    function  test() public view returns(string memory){
        return f.dahan();
    }
}

11.constant修饰符

作用与view差不多,只能读取变量,没法修改变量,目前支持int、uint、string、bytes1-32
在0.5.0版本以后被view和pure替代,用了会报错

pragma solidity ^0.5.0;
contract constantTest{
    // 测试可以支持的数据类型
    uint constant num=100;
    uint public num1=100;
    int constant num2=100;
    string constant num3="100";
    bytes1 constant num4=0x11;
    bytes constant public num5=new bytes(2);//编译没有报错,但无法修改,只能是0x0000;

    //constan全局变量无法修改,编译报错!
/*    function changeIt() public{
        num=0;
    }*/


   function change() public pure returns(string){
     num5[0]=0x12;
     num5[1]=0x12;
     return string(num5);
   }//并没有修改成功!

    function changeIt() public constant returns(uint){
        num1=200;
        return num1;
    }//返回200,但是全局变量num1依旧是100
 }

12.构造函数

新版本用 constructor(…) {…}作为构造函数

pragma solidity ^0.5.0;
 constructor ()public {
        owner = msg.sender;
        len = 0;
       
    }

13.modifier

函数修饰符函数modifier 函数名(参数列表){…}

pragma solidity ^0.5.0;
contract modifierTest{

  address  public owner;
  uint public num=0;

  constructor() public{
      owner=msg.sender;
  }

  modifier Onlyowner() {
      require(msg.sender==owner);
      _;
  }//定义的函数修饰符函数,_;在满足require的条件时,会被替换成num=100;否则函数回滚,不会执行num=100;

  function change() Onlyowner public{
      num=100;
  }//用Onlyowner修饰

 }

modifier修饰函数作用举例1

pragma solidity ^0.5.0;
contract mappingTest{
    //定义两个mapping类型的变量
    mapping(address=>uint) public idmapping;
    mapping(uint=>string) public namemapping;
    //用于代表注册的人数
    uint sum=0;

    modifier control(){
        require(idmapping[msg.sender]==0);
        _;
    }//通过这个修饰,可以确保每个账户只能注册一次!
    //注册,主要是合约调用者的地址=>id;id=>姓名两组映射关系
    function register(string memory name ) control public{
        address account=msg.sender;
        sum++;
        idmapping[account]=sum;
        namemapping[sum]=name;
    }
    //根据地址来获得id
    function getIdbyAddr(address account) public view returns(uint){
        return idmapping[account];
    }
    //根据id来获得姓名
    function getNamebyId(uint id) public view returns(string memory){
        return namemapping[id];
    }
}

modifier修饰函数作用举例2

contract modifierTest2{

    uint public level=9;

    string public name="李知恩";

    uint public age=20;
    //modifier可以有参数列表
    modifier controlLevel(uint _level){
      require(level>_level);
      _;
    }
    //不同level才能有不同的权限!
    function changename() controlLevel(10) public{
      name="IU";
    }

    function changeage() controlLevel(5) public{
      age=18;
    }

 }

一个函数可以被多个modifier函数修饰符修饰,且执行顺序非常关键。(实际上就是按照修饰符顺序,依次执行,遇到_;会将后面的modifier函数,直接嵌入替换即可!

pragma solidity ^0.5.0;
contract modifierTest3{

     uint public a;
     modifier mod1 {
         a=1;
         _;
         a=2;
     }     
     modifier mod2{
         a=3;
         _;
         a=4;
     }
     //执行顺序:a=1,a=3,a=100,a=4,a=2
     function change() mod1 mod2  public{
         a=100;
     }//最终a=2

     //执行顺序:a=3,a=1,a=100,a=2,a=4
      function change1() mod2 mod1  public{
         a=100;
     }//最终a=4

 }

14.继承
一、合约支持连续继承:儿子继承父亲,父亲继承祖父

contract father is grandfater{...}
contract son is father{...}

1、修饰变量的修饰符在继承中作用:默认、public、internal 可以被继承,private不能被继承,且修饰变量没有external
2、修饰函数的修饰符在继承中的作用:public internal、external可以被继承,private不能被继承

二、合约支持多重继承:儿子继承父亲和母亲

contract son is mom,father{
/1、继承的属性如果mon和father里面都有,那么按照继承的顺序(这里是mom,father),选择最后一个继承者(这里是father)覆盖掉其他的合约属性/

//2、自身的属性如果和is后面的父辈合约重复了,那么自身属性会覆盖掉其他合约属性

//3、继承时发生的函数重载以自身为准,父辈合约之间发生函数冲突,也是和属性一样,按继承顺序的最后一个继承者为准

}

15.合约销毁
selfdestruct(合约发布者的地址)

pragma solidity ^0.4.0;
contract selfdestructTest{

    uint public money;
    address owner;
    constructor() public{
        owner=msg.sender;
    }

    function increase() public returns(uint){
        money++;
        return money;
    }

    //销毁合约
    function kill() public{
        if(msg.sender==owner){
            selfdestruct(owner);
        }
    }
}

函数知识小结

1、private 不能被继承,不能在外部被调用,可以在内部被调用

2、internal可以在内部被调用,不能在外部被调用,可以被继承

3、external 不能在内部被调用,只能够在外部调用,可以被继承,如果强行执行,通过”地址.“

4、public权限最大,可以在外部和内部调用,可以被继承

5、pure不会读取全局变量,更不会修改全局变量,一个固定的输入就会有一个固定的输出

6、constant在函数中,和view相同,在全局变量中,只用于bytes1-32,uint,int,string代表数据不能被修改

7、view只读取全局变量的值,不修改它,不消耗gas

8、payable转账的时候必须要加的关键字

9、命名参数{形参1名字:值1,形参2名字:值2}

10、函数可以有多个返回值

public 修饰变量时,会默认生成一个getter方法

pragma solidity ^0.5.0;
contract getter{
   uint public num=100;
   //相当于生成了下面注释的getter方法,external属性的
 /*   function num() external view returns(uint){
        return num;
   }*/

   function getnum() public view returns(uint){
       return this.num();
   }

   mapping(uint=>string) public map;

   constructor() public{
          map[0]="李知恩";
          map[1]="IU";
          mapx[0][1][2]="IU";
   }
    //mapping类型比较特殊,生成的getter方法带有输入参数
/*  function map(uint key) external view returns(string){
       return map[key];
   }*/

   function getmap(uint key) public view returns(string memory){
       return this.map(key);
   }
   //复杂一点的mapping原理也是如上面所述的原理,这里带三个输入参数
   mapping(uint=>mapping(uint=>mapping(uint=>string))) public mapx;

}

solidity 高级篇

1.memor与storage
规则1 – 状态变量
状态变量总是存储在存储区storage中。

pragma solidity ^0.5.0;  

contract DataLocation {  

   // storage     
   uint stateVariable;  
   uint[] stateArray;  
}

此外,不能显式地标记状态变量的位置。

pragma solidity ^0.5.0;  

contract DataLocation {  

   uint storage stateVariable; // 错误  
   uint[] memory stateArray; // 错误  
}

规则2 – 函数参数与返回值
函数参数包括返回参数都存储在内存memory中

pragma solidity ^0.5.0;  

contract DataLocation {  

   // storage     
   uint stateVariable;  
   uint[] stateArray;  

   function calculate(uint num1, uint num2) public pure returns (uint result) {
       return num1 + num2
   }
}

此处,函数参数 uint num1 与 uint num2,返回值 uint result 都存储在内存中。
规则3 – 局部变量
值类型的局部变量存储在内存中。但是,对于引用类型,需要显式地指定数据位置。

pragma solidity ^0.5.0;  

contract Locations {  

  /* 此处都是状态变量 */  

  // 存储在storage中  
  bool flag;  
  uint number;  
  address account;  

  function doSomething() public  {  

    /* 此处都是局部变量  */  

    // 值类型
    // 所以它们被存储在内存中
    bool flag2;  
    uint number2;  
    address account2;  

    // 引用类型,需要显示指定数据位置,此处指定为内存
    uint[] memory localArray;        
  }  
}

不能显式覆盖具有值类型的局部变量。

function doSomething() public  {  

    /* 此处都是局部变量  */  

    // 值类型
    bool memory flag2;  // 错误
    uint Storage number2;  // 错误 
    address account2;  

  }

规则4 – 外部函数的参数
外部函数的参数(不包括返回参数)存储在Calldata中。

举例说明

contract memoryTest{
    //状态变量,默认是storage,且不能显示说明!
    uint[]  public arrx;
   /* uint[] memory  public arrx;
    uint[] storage public arrx;*/

    function test(uint[] array) public returns(uint){
        //array存储在memory内,将值传递给存储在storage内的arrx
        arrx=array;
    //这里arrx由于是数组,属于引用变量,因此存储都在storage里的Z和arrx等同,且数组在storage内可以修改长度
        uint[] storage Z=arrx;

        Z[0]=10;

        Z.length=10;
   //由于Y是存储在memory的,它和arrx除了赋值的关系外,并没有等价的关系,且不能修改长度,Y修改对arrx不影响    
        uint[] memory Y=arrx;
        //Y.length=20;
        Y[1]=20;
    }

     function test2() public view returns(uint[]){
         return arrx;
     }//返回整个数组内容

    function test3() public view returns(uint){
        return arrx.length;
    }//返回数组长度
}

变量类型
Solidity中,变量类型有以下几大类:

值类型
地址类型
引用类型
值类型
在这里插入图片描述
地址类型
地址类型表示以太坊地址,长度为20字节。地址可以使用.balance方法获得余额,也可以使用.transfer方法将余额转到另一个地址。

address x = 0x212;
address myAddress = this;

if (x.balance < 10 && myAddress.balance >= 10) 
    x.transfer(10);

引用类型/复合数据类型
Solidity中,有一些数据类型由值类型组合而成,相比于简单的值类型,这些类型通常通过名称引用,被称为引用类型。

引用类型包括:

数组 (字符串与bytes是特殊的数组,所以也是引用类型)
struct (结构体)
map (映射)
struct
定义与初始化

pragma solidity ^0.5.0;
contract structTest{
    //结构体的定义
    struct student{
        uint grade;
        string name;
    }
    //结构体的定义
    struct  student1{
         uint grade;
         string name;
    // student1 stu;结构体内部不能包含自己本身,但是可以是动态长度的数组,也可以是映射
         student1[] stu;
         mapping(uint=>student1) hahah;
    }


    //结构体的初始化
    function init() public pure returns(uint,string memory){
   /*struct在函数体内默认是storage,而具体画的结构体student(100, "IU");则存储在memory中,因此需要显示定义结构体指针s在memory*/
        student memory s=student(100, "IU");
        return(s.grade,s.name);
    }
    //初始化的第二种方式
    function init2() public pure returns(uint,string memory){
        student memory s =student({grade: 100, name: "IU1"});
        return(s.grade,s.name);
    }

}

结构体内的mapping

初始化结构体时不能初始化mapping,且操作结构体内的mapping需要storage类型

pragma solidity ^0.5.0;
contract structTest{
    //结构体的定义
    struct  student{
         uint grade;
         string name;
         mapping(uint=>string) map;
    } 
    student IU;//1、默认是storage类型,只有storage类型才能操作结构体内的mapping

    //结构体的初始化
    function init() public returns(uint,string memory,string memory){
      //结构体初始化不需要初始化mapping
        student memory s=student(100, "IU");
        //2、memory对象不能操作结构体内的mapping
        //s.map[0]="李知恩";编译出错
        //3、通过将memory对象s赋值给storage对象,才能操作结构体内的mapping
        IU=s;
        IU.map[0]="李知恩";
        return(s.grade,s.name,IU.map[0]);
       // return(IU.grade,IU.name,IU.map[0]);返回值一样
    }
}

结构体作为形参

前提:1、函数必须是internal 2、不能直接将形参赋值给storage指针

pragma solidity ^0.5.0;
contract structTest{

    //结构体的定义
    struct  student{
         uint grade;
         string name;

    }
    /*1、必须是internal修饰  2、结构体变量stu是storage指针,不能和memory指针直接赋值(这就好像两个指向不同区域的指针是没法赋值一样的意思)*/
    function test1(student memory s) internal{
        student memory stu=s;
    }//编译不通过
    //默认就是memory,编译不通过
  /*    function test1(student s)internal{
        student stu=s;
    }*/
    //编译通过了
    /*    function test1(student  storage s)internal{
        student stu=s;
    }*/

}

情况1:storage转storage

pragma solidity ^0.5.0;
contract structTest{
    //结构体的定义
    struct  student{
         uint grade;
         string name;   
    }
    //stu是状态变量,存储在storage区域
    student stu=student(100,"李知恩");

    function test1(student storage s)internal{
        student storage IU=s;//内存中的s指针通过test1(stu)后,指向了storage区域的stu对象
        IU.name="IU";//通过IU指针间接操作了storage区域的stu对象
    }

    function call() public  returns(string memory){
        test1(stu);//传递的实际是指向stu对象的指针
        return stu.name;
    }//返回IU

}

情形2:memory转storage

pragma solidity ^0.5.0;
contract structTest{
    //结构体的定义
    struct  student{
         uint grade;
         string name; 
    }
    //stu是状态变量,存储在storage区域
    student stu=student(10,"李知恩");

    function test1(student memory s)internal{
        stu=s;//将s的值赋给了stu,stu变为student(100,"IU")
        s.name="love";//修改memory上的s,不会影响storage上的stu
    }

    function call() public  returns(uint,string memory){
        student memory IU=student(100,"IU");//IU是局部变量,存储在memory中
        test1(IU);//值传递,将IU的值传递给了形参s(也存储在memory当中)
        return (stu.grade,stu.name);//返回100,"IU"
    }

}

情形3:storage转memory

pragma solidity ^0.5.0;
contract structTest{
    //结构体的定义
    struct  student{
         uint grade;
         string name;

    }
    //stu是状态变量,存储在storage区域
    student stu=student(10,"李知恩");

    function test1(student storage s) pure internal returns(uint,string memory){
        student memory IU=s;//在memory中,s根据传递过来的stu指针,对stu对象进行了副本copy,赋值给了IU
        IU.name="IU";//在memory内的IU进行修改,不会影响存储在storage中的stu
        return(IU.grade,IU.name);
    }

    function call() public view  returns(uint,string memory){
        test1(stu);//传递的是指向stu的指针
        return (stu.grade,stu.name);
    }//返回(10,"李知恩")
    //由于internal外部无法调用,因此采用这种方式在外部间接调用test1,结果说明内存中的IU的值发生了改变
    function test() public view  returns(uint,string memory){
       return test1(stu);
    }//返回(10,"IU")

}

情形4:memory转memory

pragma solidity ^0.5.0;
contract structTest{
    //结构体的定义
    struct  student{
         uint grade;
         string name;

    }
    function test1(student memory s) pure internal {
        student memory IU=s;//s和IU都指向了stu的存储的memory区域
        IU.name="IU";//通过IU间接修改了stu对象
        IU.grade=18;

    }

    function call() public pure  returns(uint,string memory){
        student memory stu=student(100,"李知恩");
        test1(stu);//memory上的对象stu,传递的是指向它的指针(这里比较特殊,属于本身对内存的一个优化操作)
        return (stu.grade,stu.name);
    }//返回(18,"IU")
}

enum
enum 枚举体名字{枚举1,枚举2…} 枚举1-N不能有汉字,本质上枚举1数值为0,枚举2数值为1,以此类推
枚举体主要用于状态的切换,例如从状态1切换到状态2,让代码具有可读性!

pragma solidity ^0.5.0;
contract enumTest{
    enum state{s1,s2,s3,s4,s5}

    state step=state.s1;

    function getEnum() public view returns(state){
        return step;
    }//返回uint8:0(也是看枚举个数,按最小标准存储)

    function firstStep() public  returns(string memory){
        require(step==state.s1);//必须完成s1才能切换到s2
        step=state.s2;
        return "s1 over!";
    }

    function sencondStep() public returns(string memory){
        require(step==state.s2);//必须完成s2才能切换到s3
        step=state.s3;
        return "s1,s2 are over!";
    }
}

未完待续。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值