Solidity引用类型

一.引用类型介绍

1.不定长字节数组

  • 特点
    • 动态字节数组
    • 引用类型(说明可以使用storage来修饰,进行引用传递,指针的效果)
    • 支持 下标索引
    • 支持 length、push方法 (push会帮助分配空间的)
    • 可以修改
    • 以十六进制格式赋值

注意的坑:

旧版本的remix可以直接在remix中使用"helloworld"形式给bytes赋值,新版本不允许,必须使用0x格式

pragma solidity ^0.4.25;
 
contract BytesTest { 
    //创建 usrName , USRNAME, _usrName
    bytes public _name = new bytes(1);
    bytes public _name2;
    
    function setLength(uint length) {
        _name.length = length; 
    }

    function getLength() view returns (uint) { 
        return _name.length;
    } 
    
    function setName(bytes name){
        _name = name;
        _name2 = name;
    }
    
    function changeName(bytes1 name){
        _name[0] = name;
    }
    
    function changeName2(bytes1 name) {
        //未分配空间时,调用访问下标会出错
        _name2[0] = name;
    }
    
    function pushTest() public {
        _name2.push("a");
    }
    
    function setInside(){
        _name = "helloWorld";
        _name2 = "HELLOWORLD2";
    }
}

2.字符串

  • 动态尺寸的UTF-8编码字符串,是特殊的可变字节数组
  • 引用类型
  • 不支持下标索引
  • 不支持length、push方法
  • 可以修改(需通过bytes转换)
pragma solidity ^0.4.25;
contract StringTest {
    string public _name = "Linda";
    
    function nameBytes() constant returns (bytes) {
        return bytes(_name);     
    }
    
    function nameLength() constant returns (uint) { 
        return bytes(_name).length;
    }
    
    function changeName() public {
        bytes(_name)[0] = "L";
        //_name[0] = "H"; //ERROR,字符串不直接支持下标访问
    }
    
    function changeLength() {
        bytes(_name).length = 15;
        bytes(_name)[14] = "x";
    }
}

字符串处理很不便(字符串拼接),有一个字符串插件:https://github.com/Arachnid/solidity-stringutils

import "github.com/Arachnid/solidity-stringutils/strings.sol";

contract StringTest {
    using strings for *;
    string public myStr;

    function foo(string strPart) {
        myStr = myStr.toSlice().concat(strPart.toSlice());
    }
}

3.转换

在这里插入图片描述

pragma solidity ^0.4.25;

contract ConvertTest {
    
    bytes10 b10 = 0x68656c6c6f776f726c64;     //helloworld
    //bytes bs10 = b10; //无法直接转换
    
    bytes public bs10 = new bytes(b10.length);
    
    //1. 固定字节数组转动态字节数组
    function fixedBytesToBytes() public{
        for (uint i = 0; i< b10.length; i++){
            bs10[i] = b10[i];
        }
    }

    //2.string转动态字节数组(不定长字节数组)
    string  greeting = "helloworld";
    bytes public b1;
    function StringToBytes() public {
        b1 = bytes(greeting);
    }
    
    //3. 动态字节数组转string
    string public str3;
    function BytesToString() public {
        fixedBytesToBytes();
        str3 = string(bs10);
    }

    //4. fixed bytes to String,error
    function FiexBytesToString(){
        //string tmp = string(b10);
    }
}

4.数组

4.1 内置数组

已介绍

  • string (不定长)
  • bytes(不定长字节数组)
  • bytes1…bytes32(定长字节数组)
4.2 自定义数组
4.2.1 定长数组
  • 类型T,长度K的数组定义为 T[K],例如:uint [5] numbers, byte [10] names;
  • 内容可变
  • 长度不可变,不支持push
  • 支持length方法
pragma solidity ^0.4.25;

contract ArrayTest {
    //test1:
    uint [10] value = [1,2,3,4,5];

    uint public sum;
    function getSum(){
        sum = 0;
        for (uint i = 0; i < value.length; i++){
            sum += value[i];
        }
    }
    
    function changeValue(){
        value[0] = 2; //内容可修改
        //value.length = 100; //报错,长度不可修改
    }    
    
    function getString () public view returns (string) {
        string strMsg = 'Hello';
        bytes5 strArr = strMsg;
        bytes helloworld;
        
        for (var i = 0; i < strArr.length; i++) {
            helloworld.push(strArr[i]);
        }
        return string(helloworld);
    }
}
4.2.2 不定长数组
  • 定义格式为T [ ],例如:string[ ] names, byte[ ] citys
  • 内容可以修改
  • 可以改变长度(仅限storage类型)
    支持lengthpush方法
pragma solidity ^0.4.25;

contract ArrayTest {
    
    //创建方式一:字面量形式
    uint [] public c = [uint(1), 2, 3];
    function h() public {
        c.push(4);
    }
    
    //创建方式二:new 关键字	
    //a. storage类型数组,状态变量,最初为空,下标访问时越界
    uint[] public b;
    
    //复杂类型在局部是引用
    function g(){
        b = new uint[](7);
        //可以修改数组的长度
        b.length = 10;
        b[9] = 100;
        b.push(101);
    }
    
    //b. memory类型数组
    function f() public returns (uint[]){
        uint[] memory a = new uint[](7);
        
        //不能修改长度
        //Error: Expression has to be an lvalue.
        //a.length = 100;
        //a.push(10);
       
         for (uint i = 0; i< a.length; i++){
             a[i] = i;
         }

        return a;
    }
}

5.结构体

pragma solidity ^0.4.5;

contract Test {
    //定义结构之后无分号,与枚举一致
    struct Student {
        string name;
        uint age;
        uint score;
        string sex;
    }
    
    //两种赋值方式
    Student public stu1 = Student("lily", 18, 90, "girl");//注意顺序
    Student public stu2 = Student({name:"Jim", age:20, score:80, sex:"boy"});//不需要考虑顺序
    
    Student[] public Students;
    
    function assign() public {
        Students.push(stu1);
        Students.push(stu2);
        
        stu1.name = "Lily";
    }
}

6.映射表

  • 语法:mapping(keyType => valueType)
  • 键的类型(keyType) 除了映射、变长数组、合约、枚举以及结构体以外的几乎所有类型。
  • 值的类型(valueType) 可以是包括映射类型在内的任何类型。
  • 无法判断一个mapping中是否包含某个key,因为它认为每一个都存在,不存在的返回0或false。
  • 映射可以被视作为一个哈希表,在映射表中,不存储键的数据,仅仅存储它的keccak256哈希值,用来查找值时使用。
  • 映射类型,仅能用来定义状态变量,或者是在内部函数中作为storage类型的引用。
  • 不支持length
pragma solidity ^0.4.25;

contract MappingTest {
    // id -> name
    mapping(uint => string) public id_names;
    
    constructor()  public{
        id_names[1] = "lily";
        id_names[2] = "Jim";
        id_names[3] = "Lily";
    }
    
    function getNameById(uint id)  public returns (string){
        return id_names[id];
    }
    
    function setNameById(uint id)  public returns (string){
        id_names[id] = "Hello";
    }
    
    // mapping 没有length属性
    // function getMapLength() public returns (uint){
    //     return id_names.length;
    // }
}
  • HeiMaBank

在这里插入图片描述

pragma solidity ^0.4.25;

contract HMBank{
    mapping(address => uint) public usrCount;

    function saveMoney(uint money) public {
        usrCount[msg.sender] += money;// 根据当前调用者的 address 增加 mapping中对应的值
    }
    
    function getMoney(uint money) public{
        usrCount[msg.sender] -= money;// 根据当前调用者的 address 减少 mapping中对应的值
    }
    
    function showMoney() public view returns(uint){
        return usrCount[msg.sender]; // 根据当前调用者的 address 获取mapping 里的对应值
    }
}
  • 官方案例:
pragma solidity ^0.4.25;

contract MappingExample {
    mapping(address => uint) public balances;

    function update(uint newBalance) public {
        balances[msg.sender] = newBalance;
    }
}

contract MappingUser {
    function f() public returns (uint) {
        MappingExample m = new MappingExample();
        m.update(100);
        return m.balances(this);
    }
}

7.关键字 delete

pragma solidity ^0.4.25;

contract DeleteExample {
    uint public age;
    function changeAge(uint num){
        age += num;
    }
    function deleteAge(){
        delete age;
    }
}
  • delete age 的结果是将 age 的类型在初始化时的值赋值给 age 。即对于整型变量来说,相当于 age = 0, 但 delete 也适用于数组,对于动态数组来说,是将数组的长度设为 0,而对于静态数组来说,是将数组中的所有元素重置。 如果对象是结构体,则将结构体中的所有属性重置。

  • delete 对整个映射是无效的(因为映射的键可以是任意的,通常也是未知的)。 因此在你删除一个结构体时,结果将重置所有的非映射属性,这个过程是递归进行的,除非它们是映射。 然而,单个的键及其映射的值是可以被删除的。

    理解 delete a 的效果就像是给 a 赋值很重要,换句话说,这相当于在 a 中存储了一个新的对象。

pragma solidity ^0.4.25;

contract DeleteExample {
    uint data;
    uint[] dataArray;

    function f() public {
        uint x = data;
        delete x; // 将 x 设为 0,并不影响数据
        delete data; // 将 data 设为 0,并不影响 x,因为它仍然有个副本
        uint[] storage y = dataArray;
        delete dataArray;
        // 将 dataArray.length 设为 0,但由于 uint[] 是一个复杂的对象,y 也将受到影响,
        // 因为它是一个存储位置是 storage 的对象的别名。
        // 另一方面:"delete y" 是非法的,引用了 storage 对象的局部变量只能由已有的 storage 对象赋值。
    }
}

8.数据位置

  • 存储位置
  • 值传递、引用传递

复杂类型,不同于之前值类型,占的空间更大,超过256字节,因为拷贝它们占用更多的空间,如数组(arrays)数据结构(struct),他们在Solidity中有一个额外的属性,即数据的存储位置:memorystorage

8.1 内存(memory)
  • 数据不是永久存在的,存放在内存中,越过作用域后无法访问,等待被回收。
  • 被memory修饰的变量是直接拷贝,即与上述的值类型传递方式相同。
8.2 存储(storage)
  • 数据永久保存在。
  • 被storage修饰的变量是引用传递,相当于只传地址,新旧两个变量指向同一片内存空间,效率较高,两个变量有关联,修改一个,另外一个同样被修改。
  • 只有引用类型的变量才可以显示的声明为storage
  • 状态变量是stroage类型,无法更改;
  • 局部变量(仅限数据结构或数组,string)默认是storage类型,可以声明为memory类型

在这里插入图片描述

pragma solidity ^0.4.25;
contract Test {
    string public name= 'lily';
    uint256 public num = 10;
    
    function call1() public {
        setName1(name);
    }
    
    function setName1(string src) private {
    //function setName1(string memory src) private {
        num = 100;
        bytes(src)[0] = "L";
    }
    
    function call2() public {
        setName2(name);
    }
    
    function setName2(string storage src) private {
        num = 1000;
        bytes(src)[0] = "L";
    }
    
    function localTest () public view returns(uint){
        string memory tmp = name;
        //string storage tmp = name;
        uint x = num;
        x= 10000;
        return x;
        bytes(tmp)[0] = "L";
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值