solidity学习笔记

solidity语法接近于Javascript,是一种面向对象的语言,是一种真正意义上运行在网络上的去中心合约。一些零散知识,记录下,如有不正确欢迎指出,努力学习中!部署用的是Remix在线编辑器。

一、初识solidity

  • 版本申明
    pragma solidity ^0.4.0;

  • 说明
    版本要高于0.4才可以编译

1.pure与view关键字

只要有了pure与view修饰符的函数,那么调用函数就不会消耗gas
view: 可以自由调用,因为它只是“查看”区块链的状态而不改变它,函数输出可以在调试时直接显示;
pure: 也可以自由调用,既不读取也不写入区块链


2.整型数据

关键字:int,uint,intx,uintx(x为整形位数,如uint8范围为0~255)


3.固定长度字节数组

关键字:bytesX(X为1~32),代表X字节,最好用0x来赋值
用.length获取长度,即X的值
只能整体修改,不能单独改某位

  • 强制转换:压缩时是从前往后截取,扩充是从最后补充位
    function change() public returns(bytes1){
        by = 19;
        by[0] = 1;//报错,不能单独改某一位
        return by;
    }

4.动态长度字节数组

建立需要new

    bytes public byy=new bytes(2);
    function initbyy(){
    //动态的可以用下标赋值
        byy[0]=0x01;
        byy[1]=0xfd;
    }
    function changelength(){
        byy.length=5;//往后补0扩充位数
        byy.push(0x99);//在最后加入,0x01fd00000099
    }

5.string

不区分单双引号,只能利用bytes间接改与获取长度,注意处理中文与特殊字符(¥%&等),一个中文字符占3个字节,特殊字符还是占一个字节。

    string Myname = "bobo";
    function getmynamelength()view returns(uint){
        return bytes(Myname).length;
    }
    function changemyname(){
        bytes(Myname)[0]='L';
    }
  • 固定转动态bytes,动态bytes与string互转:
	string Myname = "bobo";
	function bytesx2bytes()view returns(string){
	    //函数体内部声明变量要加memory关键字申请内存
	    bytes memory name2=new bytes(bytes(Myname).length);
	    for(uint i=0;i<bytes(Myname).length;i++){
	        name2[i]=bytes(Myname)[i];
	    }
	    return string(name2);
	}

6.固定数组

长度固定,不能改长度,不能push元素,可以用length获取长度;

contract Fixarray{
    uint[5] public arr=[1,2,3,4,5];
    function init()public{
        arr[0]=100;
        arr[1]=200;
    }
    function getlength()returns(uint){
        return arr.length;
    }
}

7.可变长度数组

定义时不指定长度,就可以随意添加更改数组,可以用push增加元素;

    uint[] grade=[1,2,3];
    function getContent() view returns(uint[]){
        return grade;
    }
    function additem(){
        grade.push(6);
    }
    function getlength()returns(uint){
        return grade.length;
    }
    function changelength(){
        grade.length=2;
    }

8.二维数组

定义固定长度时和其他一些语言行列是相反的,如下面例子uint[2][3],是有三行二列。但获取元素是正常的,和其他语言一样。

    uint[2][3] map=[[1,2],[3,4],[5,6]];//这里2是列,3是行
    function getContent2() view returns(uint[2][3]){
        return map;
    }
    function getitem()view returns(uint){
        return map[1][0];//output 3;map[2][0]=5
    }
    function getlength2() view returns(uint){
        return map.length;//output 3
    }
    function getlength3() view returns(uint){
        return map[0].length;//output 2
    }

可变长度二维数组和可变数组定义类似。


二、以太坊账户


1.address关键字

Payable用于转账,balance获取账户金额

contract addr{
    address public account = 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4;
    function changeitem()view returns(uint160){
        return uint160(account);
    }
    //给此账户转账
    function pay() payable{
        
    }
    //获取此账户金额
    function getbalance() view returns(uint){
        return this.balance;
    }
}

2.合约与合约账户

solidity学习笔记_第1张图片

  • Gas字面意思是:瓦斯、汽油,是一种燃料。当你在以太区块上发送token、执行合约、转移币或做其他事情时,计算机需要进行计算,这个计算过程需要消耗网络资源。你必须支付“燃料费”(即Gas),才能让计算机为你工作,让矿工为你处理交易
  • 账户之间转账,用msg全局变量获取转账金额,即value的值,也可以直接输入金额,如10 ether,finney,gwei,wei:
    //每个账户地址都有对应的transfer方法用于账户间转账
    function trans()payable{
        address acco=0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2;
        acco.transfer(msg.value);
        //acco.transfer(10 ether);
    }

3.以太坊中的一些全局变量

//返回给定区块号的哈希值,只支持最近256个区块,且不包含当前区块。在版本0.4.22种弃用并替换为blockhash(uint blockNumber)
 block.blockhash(uint blockNumber)returns(bytes32)
//当前块矿工的地址
block.coinbase(address)
//当前块的难度
block.difficulty(uint)
//当前区块的块号
block.number(uint)
//当前块的Unix时间戳(从1970/1/1 00:00:00 UTC开始所经过的秒数)
block.timestamp(uint)
//完整的调用数据calldata
msg.data(bytes)
//当前还剩的gas,0.4.21版本弃用并替换为gasleft()
msg.gas(uint)
//当前调用发起人的地址
msg.sender(address)
//调用数据calldata的前四个字节
msg.sig(bytes4)
//这个消息所附带的以太币,单位为wei
msg.value(uint)
//当前块的时间戳(block.timestamp的别名)
now(uint)
//交易的gas价格
tx.gasprice(uint)
//交易的发送者(全调用链)
tx.origin(address)

4.转账误操作

使用合约转移指定以太币,首先得合约有钱,之后才能转;在value转移多于合约内指定的以太币,多余的以太币将保存在合约中。
低层send方法,和transfer方法一样,但send比较危险,一些异常交易不会报错。

5.mapping映射——哈希表

pragma solidity ^0.4.0;
contract maptest{
    mapping(address=>uint) idmapping; //0x5B38Da6a701c568545dCfcB03FcB875f56beddC4 =>1
    mapping(uint=>string) namemapping;//1=>name
    uint sum=0;
    //建立映射
    function register(string name){
        address account=msg.sender;
        sum++;
        idmapping[account]=sum;
        namemapping[sum]=name;
    }
    
    function getID_by_address(address are)view returns(uint){
        return idmapping[are];
    }
    function getname_by_ID(uint id)view returns(string){
        return namemapping[id];
    }
}

三、实战


1.函数重载

函数名相同,但传递参数不同(类型,数量),则称为函数重载。
当传入的参数不能区分是到底调用哪个重载的函数时编译报错,常常出现在虽然两个类型不同,但范围相同的关键字, 如uint160和address等

2.函数命名参数

有如下一些方式传递参数

pragma solidity ^0.4.0;
contract test{
    uint public n;
    string public na;
    function setparam(uint num,string name){
        n=num;
        na=name;
    }
    function test1() {
        setparam(10,"bobo");
    }
    function test2(){
        setparam({num:10,name:"bobo2"});
    }
    function test3(){
        setparam({name:"bobo2",num:10});
    }
    
}

3.变量生命周期与作用域

作用域遵行就近原则,函数内变量生命周期就是函数执行开始到结束。


4.external权限修饰

使用external只能在外部调用,继承的合约也不能调用。可以使用this.来调用,这个属于外部调用。可以在一个新建合约创建那个含外部调用方法的合约,实现外部调用方法的调用。

pragma solidity ^0.4.0;
contract father{
    function test() external view returns(string){
        return "test";
    }
    //外部调用修饰符修饰的函数不能内部调用,下面注释的代码报错
    // function dotest0()public view{
    //     test();
    // }
    //可以使用this.来调用
    function dotest()public view{
        this.test();
    }
}
contract son is father{
    //继承的合约也不能在内部调用有外部调用修饰符修饰的函数,下面代码报错
    //  function dotest0()public view{
    //     test();
    // }
}
//新建合约可以调用
contract doexternal{
    father f= new father();
     function dotest0()public view{
        f.test();
    }
}

5.值传递与副本拷贝

pragma solidity ^0.4.0;
contract zhichuandi{   
    uint public a=100;
    uint public b=a;
    function change(){
        b=999;//a不变
    }
    //传递形参时这里传递的是副本,a本身不变
    function change2(uint m)returns(uint){
        m++;
        return m;
    }
    function test(){
        change2(a);
    }
}

6.废弃的constant静态修饰

  • 修饰函数的constant在4.0版本中与view关键字修饰等价,代表的是常量,不能修改,5.0版本将废弃。
  • 全局变量有constant属性,局部变量没有,不可修改。

7.构造函数

  • 函数名与合约名相同,合约一旦部署后就会自动调用一次。
  • 构造函数只能有一个,构造函数可以有参数
  • 可以用关键字construction直接定义构造函数,同样可以传参。
  • 常用于初始化合约地址。
pragma solidity ^0.4.0;

contract gouzao{
    address public ad;
    uint public a;
    //方式1
    function gouzao(){
        a=100;
    }
    //方式2
    //构造函数可以有参数
    function gouzao(uint aa){
        a=aa;
    }
    //方式3
    constructor(){
        a=100;
        ad=msg.sender;
    }
}

8.modifier——函数修改器

  • 可以用modifier实现用户重复注册的问题,同样用require函数。
  • 常常用modifier让合约更加简洁,只用modifier实现判断就能省去很多语句,不用每个函数都要写require;
pragma solidity ^0.4.0;
contract modifiertest{
    address public owner;
    uint public num=0;
    constructor(){
        owner=msg.sender;
    }
    //定义modifer
    modifier onlyowner{
        require(msg.sender==owner);//判断当前地址是否是合约拥有者
        _; //动态添加
    }
    //附加上modifer,先执行modifer的第一条语句
    //即判断当前地址是否是合约拥有者,通过则执行下列语句,否则报错
    function change(uint _num) onlyowner{
        num=_num;
    }    
}

9.多个modifer执行先后顺序?

下面分析告诉你答案,意会吧。

contract mulmodifier{
    uint public a=0;
    
    modifier mod1{
        a=1;
        _;
        a=2;
    }
    
    modifier mod2{
        a=3;
        _;
        a=4;
    }
    //下划线是一个整体
    //a=1 -> a=3 -> a=100 -> a=4 -> a=2;
    function test() mod1 mod2{
        a=100;
    }
}

10.合约的继承

pragma solidity ^0.4.0;
contract grandfather{
    uint public company=10000;
}

contract father is grandfather{
    uint public money =1000;
    function house() public returns(string){
        return "house";
    }    
}

contract son is father{
    function getmoney()returns(uint){
        return money;
    }
    function test()returns(string){
        return house();
    }  
}

11.合约的遗传特性

  • 只能继承没有修饰的,public,internal的相关属性,private属性不可继承
  • public,internal,external修饰的函数可以被继承,external要用this.来调用,private修饰的函数不可继承
  • public:内部外部都可调用 Internal:只能内部调用 External:只能外部调用
  • pure:不会读取全局变量,也不会修改全局变量,一个固定输入就会有一个固定输出
  • Constant:在修饰函数时,与view相同,在全局变量中,只用于bytes1-bytes32,uint,int,string,数据不可修改
  • View:只读取全局变量的值,不修改,不消耗gas
  • Payable:转账必须加的关键字

12.全局变量自动getter函数

在定义全局变量时,相当于定义了一个与变量名一致的getter函数,用于获取此变量的值,需要外部调用,即this.变量名()


13.继承中的重载

当子合约与父合约的属性相同时,会发成重载,覆盖掉父合约的属性。
函数也一致,会覆盖重名的父合约的函数。


14.多重继承

如果子合约有与父亲和母亲一样的属性,同样会覆盖掉。

contract father{
    uint public money=1000;
}

contract mother{
    uint public money=1200;
}

//继承顺序影响着相同属性的继承,下面例子输出1200
contract son is father,mother{
    function getmoney()returns(uint){
        return money;
    }
}

15.合约的销毁——析构函数

contract destruce{
    address public owner;
    constructor(){
        owner=msg.sender;
    }
    function kill{
        if(msg.sender==owner){
            selfdestruct(owner)
        }
    }
}

16.storage与memory的区别

  • 当引用类型作为函数参数时,它的类型默认为memory,函数参数为memory类型的变量给一个变量赋值时,这个变量的类型必须和函数参数类型一致,任何函数参数当它的类型为引用类型时,这个函数参数都默认为memory类型,memory类型的变量会临时拷贝一份值存储到内存中,当我们将这个参数值赋给一个新的变量,并尝试去修改这个新的变量的值时,最原始的变量的值并不会发生变化
  • 当函数参数为memory类型时,相当于值传递,而storage类型的函数参数将是指针传递
    如果想要在函数中通过传递过来的指针修改最原始变量的值,那么必须将函数参数的类型显示设置为storage类型,storage类型拷贝的不是值,而是指针。
  • 当我们的函数参数如果为storage类型时,函数的类型必须为internal或者private
  • 不加修饰的形参都是值传递,即memory传递,用完即销毁。
  • 数组传递的时地址,调用修改原来的值也会变。在函数内部定义的数组,合约内定义的结构体,默认是storage类型。

17.结构体定义与初始化

  • 两种方式初始化
contract memorytest{
    struct student{
        uint id;
        string name;
    }
    function test(student s)internal{
        student memory ss=s;
    }
    
    function init()returns(uint,string){
        //student memory s = student(100,'bobo');
        student memory s = student({id:100,name:'bobo'});
        return (s.id,s.name);
    }
}
  • Memory的对象不能直接操作结构体的mapping类型
  • 结构体作为参数必须是内部引用

19.努力继续学习中。。。

你可能感兴趣的:(编程学习,以太坊,区块链,智能合约)