DAPP开发(四)——Solidity语法

整型uint 和 int

uint 是默认指uint256,只能是非负的整数,以太坊严格规范,金额类型的数据都使用uint

int 默认是int256,可正可负。

uint和int之间不能相互转化

对于智能合约一定要合理安排整数类型声明,防止整数溢出的问题。


固定长度bytes类型

和uint是一样的,bytes默认是bytes1,byte1相当于uint8,bytes可以从1写到bytes32,bytes32其实就是uint256,它的长度其实就是对应的后缀数字,1-32,以一增加,但是其长度属性是不可以修改的。

动态长度bytes类型

定义方式需要bytes对象

bytes name = new bytes(2) //定义一个长度为2的bytes类型

可以修改每个值,也可以修改长度,或通过数组的push方法也可以改变长度

name[0] = 0x7a
name[1] = 0x88
name.length=5
name.push(0x99)

bytes类型一般用于十六进制的数据存储,固定长度bytes数据可以进行长度的转换,通过bytes1,截取长度为1,bytes2截取长度为2,如果截取长度大于该bytes字节的长度,那么补0。

bytes name = 0x7a68
bytes1(name) //0x7a
bytes2(name) //0x7a68
bytes3(name) //0x7a6800

固定长度bytes数据可以转动态长度字节数组,通过new bytes(bytes)的方式,然后创建一个新的动态长度bytes对象,通过for循环的形式将每个字节添加到新的bytes对象即可。

bytes2 name = 0x7a68
bytes newName = new bytes(name.length)
for(uint i;i<name.length;i++){
    newName[i] = name[i]
}

 string类型

string类型不能直接获取长度,必须通过转为bytes类型后才可以,通过bytes的[index]获取内容,获取到的是十六进制的数据,修改单个字节也是通过bytes类型。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract Demo{
    string name = "name";
    function getLength() view public returns (uint){
        return bytes(name).length;
    }
    function getName() view public returns (bytes memory){
        return bytes(name);
    }
    function changeName() public returns (string memory){
        bytes(name)[0] = 'L';
        return name;
    }
}

如果name为汉字,那么所一个汉字占三个,特殊字符占一个字节。 

动态长度bytes转string:

bytes name = new Bytes(2);
name[0] = 0x7a;
name[1] = 0x68;
function transfer() public view returns(string){
    return string(name);
}

固定长度bytes转string:分两步,固定长度bytes转为动态长度bytes,将动态长度bytes转为string

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract Transfer{
    bytes2 name = 0x7a68;
    function changeToDynamic() public returns (string memory){
        bytes memory newName = new bytes(name.length);
        //转为动态数组
        for(uint i=0;i<name.length;i++){
            newName[i] = name[i];
        }
        return this.transferTostring(newName);
    }
    function transferTostring(bytes memory  _name) public returns (string memory){
        return string(_name); //动态长度字节转为字符串
    }
}

固定数组

可以获取长度,但是长度length是不可以改变的

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract Array{
    uint [5] arr = [1,2,3,4,5];

    function Init () public{
        arr[0] = 1;
        arr[1] = 100;
    }
    
}

动态数组:

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract Array{
    uint [] arr = [1,2,3,4,5];
    function Init () public{
        arr[0] = 1;
        arr[1] = 100;
    }
    function getArray() view  public returns (uint [] memory){
        return arr;
    }
    function changeLength() public {
        arr.push(); //添加长度
    }
    function changeLength2() public {
        arr.pop(); //减少数组长度
    }
}

固定长度二维数组

定义语法法与其他语言有区别,一维数组的长度取决于后一个长度参数,二维长度取决于第一个参数,但是获取时和其他的一致,下面固定长度的也不能修改长度:

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract TwoArray {
    uint256[2][3] arr = [[1, 2], [3, 4], [5, 6]];

    function add() view public returns (uint256) {
        uint256 sum=0;
        for (uint256 i = 0; i < arr.length; i++) {
            for (uint256 j = 0; j < arr[0].length; j++) {
                sum += arr[i][j];
            }
        }
        return sum;
    }
    function change() public returns (uint256 [2][3] memory) {
        for (uint256 i = 0; i < arr.length; i++) {
            for (uint256 j = 0; j < arr[0].length; j++) {
               arr[i][j]=4;
            }
        }
        return arr;
    }
}

 动态长度二维数组

可修改长度。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract TwoArray {
    uint256[][] arr = [[1, 2], [3, 4], [5, 6]];

    function add() public view returns (uint256) {
        uint256 sum = 0;
        for (uint256 i = 0; i < arr.length; i++) {
            for (uint256 j = 0; j < arr[0].length; j++) {
                sum += arr[i][j];//求和
            }
        }
        return sum;
    }

    function change() public returns (uint256[][] memory) {
        for (uint256 i = 0; i < arr.length; i++) {
            for (uint256 j = 0; j < arr[0].length; j++) {
                arr[i][j] = 4; //改值
            }
        }
        return arr;
    }

    function getData() public view returns (uint256[][] memory) {
        return arr;
    }
    //修改长度
    function changeLength() public returns (uint256[][] memory) {
        arr[0].push(3); 
        arr[1].pop();
        return arr;
    }
}

数组字面量:

不可以修改数组的内容以及长度 。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract Array{
    function getArray() pure public returns ( uint8[3] memory){
        return [1,2,3];
    }
    function getArray2() pure public returns ( uint[3] memory){
        return [uint(1),2,3];
    }
}

上图使用字面量的形式返回数组,在定义数组时,uint会默认最小匹配原则,从uint8开始,如果存储数据超过255,则使用uint256,所以在上述function的返回值中,使用uint8数据类型。第二种方式可以指定存储数据的类型,给数组第一个元素添加即可,剩下的元素则默认为uint数据,此时默认是uint256。

address类型

在比特币的网络中是没有账户地址的概念,但在以太坊中是有的,包括合约账户地址和外部账户地址,是一串16进制的数据字符串。address是通过uint160进行存储的,两种类型可以相互强制转化,地址之间是可以进行比较大小的。

address account = 0x4D26100f60525C5Ef35DA5B4dFc7B80d55050574

uint160 num = uint160(account)

address addr = address(num)

payable,支付功能(remix调试)

380b3713b2094e3e98ab45b075391109.png

f42c3b2a47d1464eb5cc1a6d320c4320.png 函数中需要使用payable关键字,我们可以通过这个函数给合约地址转账,这里的金额为wei的单位。可以看到当前的this其实就是当前合约的地址,既然我们可以通过this.balance获取余额,那么我们也可以使用地址来获取不同账户的余额。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract PayTest{
    function pay() payable  public {

    }
    function getBalance() public view returns(uint){
        return address(this).balance;
    }
    function geThis() public view returns(address){
        return address(this);
    }
    function getOtherBalance(address account) public view returns(uint){
        return account.balance;
    }
}

 给不同的账户进行转账

function transfer() public payable {
        address account = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB;//其他的账户
        payable (account).transfer(msg.value);
    }

如果此transfer函数函数内部函数体为空,那么就会默认转到当前的合约账户,同时也可以修改为有参数的函数,不使用msg.value。如果出现msg.value的以太币数量大于代码里转账的以太币数量,那么多余的数量会直接转至当前合约账户。

常用全局变量

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract Global{
   function getDlobal() public view returns (address){
       return msg.sender;
   }
}

msg.sender(address):合约调用者账户地址。

msg.data(bytes):完整的调用数据。

msg.sender(address):当前调用发起人的地址。

msg.value(address):这个消息所携带的以太币,单位是wei。

block.coinbase(address):当前矿工地址。

block.difficulty(uint):当前矿的难度。

block.number(uint):当前区块号。

block.timestamp(uint):时间戳。

now(uint):当前块的时间戳。

tx.gasprice(uint):交易的gas价格。

tx.origin(address):交易的发送者。


send函数

function send() public payable returns(bool){
        address account = 0x78731D3Ca6b7E34aC0F824c42a7cC18A495cabaB;
        return payable (account).send(msg.value);
    }

一般不用,底层函数会有点问题,有时候出现问题也不会报错,与transfer的用法一样。

调用递归深度不能超过1024,如果gas不够会执行失败,使用这个方法要检查结果是否成功,transfer相对比较安全。

mapping 映射

一对一的映射关系,一个mappig对象可以有多个数据,但是必须遵循相应的映射关系。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract Mapping{
    mapping(address => uint) id;
    mapping( uint => string) name;
    uint sum = 0;

    function register(string memory _name) public {
        sum++;
        id[msg.sender] = sum;
        name[sum] = _name;
    }
    function getId(address _address) public view  returns (uint){
        return id[_address];
    }
    function getName(uint _id) public view returns (string memory){
        return name[_id];
    }
}

函数

遵循以下模版条件

function  funName(paramType paramName) { public | internal | external | public} [pure | constant | view | payable] [ returns (type) ] 

重载:函数名相同,传入参数不同会有不同的功能,不考虑函数返回值是否相同。但是要注意如果参数类型为uint以及uint8,如果传参255以内的数据,会出现报错的情况,因为两者都包含对应的数据,如果传参256就不会报错了,uint8的最大值就是255。

还有个特殊的类型,uint160和address类型本质是一样的,所以这两个无论何时报错的。不可以作为判断的条件

function test(address _address) public{}
function test(uint160 _id) public{}

function test2(uint id) public{}
function test2(uint8 id) public{}

函数传参

在进行传参时,可以正常传参,也可以以对象的形式传参(可以不考虑传参的顺序),在直接调用时可以只传一个参数(函数设置两个参数),但是如果在其他函数中调用时,必须严格遵守传入所有的参数。

函数返回值

返回值要有数据类型,同时可以有参数名称,也可以没有,有个小技巧,修改函数参数不用return。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract Return{
    function mul() public pure returns(uint num){
        num = 100;
    }
    function mul2() public pure returns(uint num){
        num = 100;
        return 10
    }
    function getResult(uint a,uint b) public pure returns(uint add,uint x){
        add = a+b;
        x = a*b;
    }
}

如果Return和参数修改都存在的话,以Return的值为准,同时可以返回多个参数,可以进行简化:

function getResult(uint a,uint b) public pure returns(uint add,uint x){
        return (a+b,a*b)
    }

可以用于值的交换。

作用域

同一作用域中不能重复定义相同变量,遵循作用域和作用域规则。函数内部创建变量,默认是storage类型,但是storage类型不会释放,储存在链上,所以我们要声明存储类型,一般为memory或calldate,调完函数就直接释放。

权限修饰

public,internal,external函数可以被继承,private只能合约内部使用,不能被继承,外部不能被调用。

public在合约的内部外部,子合约都可以调用。

internal不能被外部调用,但是可以在合约内部以及子合约的内部使用。

external:只能在合约外部调用,继承的合约内部也不可以调用。但也可以通过this来调用,在这里是通过地址来调用,原理也是通过外部的形式调用。

在new关键字下创建的合约对象也可以使用external方法。

继承:

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract father{
    function test() public pure returns(string memory){
        return "Father";
    }
}


contract son is father{
    function oops() public pure returns(string memory){
        return test();
    }
}

contract Test{
    father f =new father();
    function opt() public view returns(string memory){
        return f.test();
    }
}

constant关键字 

函数内部的constant关键字,在4.0版本中和view关键字等价,在5.0版本被废弃,不消耗gas,所以只做了解。

可以定义全局常量,值不可以修改,局部常量是不存在的。也支持bytes-bytes32的字节类型。

function opt() public constant returns(string memory){
        return f.test();
    }



uint constant num = 100

bytes32 constant num2 = 0x78

构造函数constructor

构造函数只能有一个,在合约创建时执行一次,类似于初始化,可以接受参数,为参数做初始化的作用

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract Transfer{
    uint public a;
    constructor(uint _a){
        a=_a;
    }
}

函数modifier

能够为函数附加判断条件

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract Transfer{
    address public owner;
    uint public num;

    constructor(){
        owner = msg.sender;
    }

    modifier OnlyOwner{
        require(msg.sender == owner);
        _;
    }
    //附加了modifier,会先执行判断当前操作人是否是合约的部署者,如果不是就不会执行下面代码
    function changeNum(uint _num) public OnlyOwner{
        num = _num;
    }
}

之前写过的mapping注册的方法,由于我们可以不断的使用同一个账号注册,导致对应的id在一直不停地变化,所以应该是一个账号注册一条信息,所以我们可以添加限制条件判断:

1.直接在函数内部使用require判断。

2.使用modifier来添加限制。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract ModifierMappig{
    mapping(address => uint) id;
    mapping( uint => string) name;
    uint sum = 0;
    modifier control{
        //require(id[msg.sender]==0);
        _;
    }
    function register(string memory _name) public control{
        require(id[msg.sender]==0);
        sum++;
        id[msg.sender] = sum;
        name[sum] = _name;
    }
    function getId(address _address) public view  returns (uint){
        return id[_address];
    }
    function getName(uint _id) public view returns (string memory){
        return name[_id];
    }
}

modifier也可以传递参数来添加限制条件: 

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract Modifier2{
    string name;
    uint level = 3;
    modifier control(uint needLevel){
        require(level>needLevel);
        _;
    }
    function changeName() public control(2){
        require(level>2);
        name = "jerry";
    }
}

函数可以有多个modifier,modifier执行顺序:

下图a =100会替换 _; 所以a最终的值为2.

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract Modifier2{
    uint public a = 0;
    modifier control{
        a=1;
        _;
        a=2;
    }
    function test() public control{
        a=100;
    }
}

 多重modifier执行:

顺序为:a=1 a=3 a=100 a=4 a=2 具体的执行顺序可以理解为一个袋状,有点像二次函数抛物线的感觉,函数体的代码就像是在顶点处纸执行,先正向走每个modifier的上层(_; 以上的代码),然后是最后一个modifier的_;(也就是函数体代码);最后逆向执行每个modifier的下层(_; 以下的代码)。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract Modifier2{
    uint public a = 0;
    modifier control{
        a=1;
        _;
        a=2;
    }
    modifier control2{
        a=3;
        _;
        a=4;
    }
    modifier control3{
        a=5;
        _;
        a=6;
    }
    function test() public control control2{
        a=100;
    }
}

 合约继承 

使用is关键字,可以继承父亲的属性,包括函数

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract father{
    uint money = 10000;
    function dahan() public pure returns(string memory){
        return "father";
    }
}


contract son is father{
    function getMoney() view public returns (uint){
        return money;
    }
    function test() public pure returns(string memory) {
        return dahan();
    }
}

合约连续继承,可以多层的被继承。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract grandFather{
    uint gudong  = 200;
}

contract father is grandFather{
    uint money = 10000;
    function dahan() public pure returns(string memory){
        return "father";
    }
}


contract son is father{
    function getMoney() view public returns (uint,uint){
        return (money,gudong);
    }
    function test() public pure returns(string memory) {
        return dahan();
    }
}

继承中的权限:

合约中的不添加任何修饰符,默认都是可以被继承的。

public属性:属性和函数能够被继承

internal属性:属性和函数能够被继承

external:属性不存在此修饰符,函数中只能被外部访问,函数也可以被继承,可以通过this来调用。

private:私有属性,只有父亲合约可以访问使用,属性和函数都不能被继承访问。

pure:函数修饰符,不会读取全局变量,更不会修改全局变量。


getter函数

全局变量如果添加了pubic修饰符,那么会自动默认生成一个get方法供外部使用,等价于一个同名external函数。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract getter{
    uint public num = 100;
    mapping (uint=>string) public map;

    function test() external view returns(uint){
        return this.num();
    }
    function test2() external{
        map[2] = "Jerry";
    }
    function test3() external view returns(string memory){
        return this.map(2);
    }
}

复杂mappng:

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract getter{

    mapping (uint=>mapping(uint=>mapping(uint=>string))) public map;

    function test() public {
        map[0][1][2]="Jerry";
    }
}

重写(继承重载)

子合约能够重写父合约的方法,直接定义相同名称的属性或方法即可。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract father {
    function dahan() virtual public pure returns(string memory){
        return "father";
    }
}


contract son is father{
    function dahan() override pure public returns (string memory){
        return "son";
    }
}

注意以下几点: 

  1. 被重写的函数必须标注为virtual,表示当前合约的继承者可以重写该函数。
  2. 重写别人的函数必须标注为override,表示我重写了父合约的函数。
  3. 对于属性的重写在新版中已经不允许修改,会报变量已经声明的错误。

多重继承

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract father {
    uint money=100;
    function dahan() public pure returns(string memory){
        return "father";
    }
}
contract mother {
    uint car=100000;
}


contract son is father,mother{
    
}

多个继承属性以及方法的重复,在新版中也会报错,需要再子合约内部重写该方法。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract father {
    uint money=100;
    uint car = 3;
    function dahan() virtual  public pure returns(string memory){
        return "father";
    }
}
contract mother {
    function dahan() virtual  public pure returns(string memory){
        return "mother";
    }
}


contract son is father,mother{
    function dahan() override(father,mother)  public pure  returns (string memory){

    }
}

父子合约的传参 

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract father {
    uint money;
    constructor(uint _money){
        money=_money;
    }
}


contract son is father(1000){
    
}

contract son2 is father{
    constructor (uint _money) father(_money){

    }
}

析构

只有合约的拥有者才能清理删除合约,要注意在使用自毁函数selfdestruct()时,参数必须为payable。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract trash{
    address payable  owner;
    constructor(){
        owner = payable(msg.sender);
    }
    function kill() public {
        require(msg.sender==owner);
        selfdestruct(owner);
    }
}

struct结构体 

类似于js中的object对象

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract Struct{
    struct student {
        string name;
        uint age;
    }
    struct student2 {
        //student stu 不能包含自己本身,但是可以是动态长度的数组,也可以是映射
        student2[] stu;
        mapping(uint=>student2) haha;
    }
    function getInfo() public  pure returns(string memory,uint){
        student memory s = student({name:"Jerry",age:20});
        //student memory s = student("Jerry",20);
        return (s.name,s.age);
    }
}

 在新版本中,mapping不建议放在struct中,被认为是不安全的,会报错。函数里的变量没有声类型时,还是storage类型,此时形参是memory类型不能赋值给函数变量。

结构体传参:

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract Struct{
    struct student {
        string name;
        uint age;
    }
    function test(student memory s) public returns (uint, string memory){
       stu = s;
       return (stu.age,stu.name);
    }
    function init () public returns(uint,string memory){
        student memory a=student({name:"jeeu",age:20});
        return test(a);
    }
}

 storage和memory存储的空间不同,修改值不会影响到另一个。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract Struct{
    struct student {
        string name;
        uint age;
    }
    student stu;
    function test(student memory s) public returns (uint, string memory){
       stu = s;
       stu.name="iiii";
       return (stu.age,stu.name);
    }
    function init () public returns(uint,string memory){
        stu=student({name:"jeeu",age:20});
        return test(stu);
    }
}

memory数据之间是通过地址来进行赋值传递,如果其中一个属性发生变化,另一个值也会变,是引用类型(包括形参)。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract Struct{
    struct student {
        string name;
        uint age;
    }
    student stu=student('gg',33);
    function test(student memory s) pure public returns (string memory){
       student memory b  = s;
       b.name="iiii";
       return (b.name);
    }
    function init () public pure returns(string memory){
        student memory a=student({name:"jeeu",age:20});
        test(a);
        return a.name;
    }
}

枚举

枚举enum,必须要有成员对象,不能存在汉字,不能写分号,适用于状态转移。返回的索引是uint

类型,匹配最小原则,从uint8开始匹配。

// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;

contract enumTest{
    enum girl{chifan,heshui,shuijiao}
    girl dateGirl = girl.heshui;
    function getEnum() public pure returns (girl){
        return girl.chifan;//获取到索引
    }
    function getDate()public view returns (girl){
        require(dateGirl==girl.heshui);
        return dateGirl;
    }
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Goat恶霸詹姆斯

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值