solidity学习笔记(6)—— 数据类型详解和转换

目录:

1、整型(int)
2、地址类型(address)
3、枚举类型(enum)
4、结构体(struct)
5、映射(mapping)
6、数组(array)
7、字符串和字节数组(string、bytes)

Solidity有如下类型的数据:

值类型:布尔类型(bool)、整型(int)、地址类型(address)、定长字节数组(bytes)、枚举类型(enum)、函数类型(function);

引用类型:字符串(string)、数组(array)、结构体(structs)、映射(mapping)、不定长字节数组(bytes)

这里需要注意的有如下几种:

1、整型:
solitity中的整型与JavaScript不同,该类型支持有符号(int)或无符号(uint)两种类型,也支持从uint8到uint256,以及从int8到int256等多种类型。

2、地址类型(address)
以太坊中的地址的长度为20字节,一字节等于8位,一共160位,所以address其实亦可以用uint160来声明。

3、枚举类型(enum)
一个变量可能的取值都知道,就可以把它定义为枚举型,然后把变量的值一一列出来,令变量的值只限于列举出来的值的范围内。例如月份、星期几、天气等情况,枚举类型用enum关键字来定义。

例子:

enum weekday{sun,mon,tue,wed,the,fri,sat};

上面声明了一个枚举类型weekday(注意,这是一个类型,而不是一个变量,你可以用这个类型去声明变量),花括号中的sun mon等称为枚举元素,weekday的值只能是括号内的七个值。

在程序中可以用weekday.sun这种格式来引用枚举类型的值。

枚举元素的值在没有指定的情况下,第一个默认为0,往后的自增一,例如sun=0,mon=1,tue=2。有指定值的时候就用指定值,其后元素如果没有指定则自增一。

4、结构体(struct)

pragma solidity ^0.4.4;
​
contract Students {
    struct Person {
        uint age;
        uint stuID;
        string name;
    }
}

上面定义的Students是一个结构体类型,不是变量。我们要声明一个结构体变量,要用下面的方法:

pragma solidity ^0.4.4;
​
contract Students {
    struct Person {
        uint age;
        uint stuID;
        string name;
    }
    // 初始化一个storage型的结构体变量_person
    Person _person = Person(18,101,"liyuechun");
    
    // 初始化一个memory类型的结构体变量person
    function personInit() {    
        Person memory person = Person({age:18,stuID:101,name:"liyuechun"});
    }    
}

5、映射
字典/映射其实就是一个一对一键值存储关系。
语法:mapping(_KeyType => _ValueType)
举个例子:
1. 映射的数据结构

{age: 28, height: 178, name: liujing} 
// 这就是一个映射,满足_KeyType => _ValueType之间的映射关系,age对应一个28的值,height对应178,name对应liujing。

2. 映射类型数据的声明

contract MappingExample {    
    mapping(address => uint)  balances;
    function update(address a,uint newBalance) public {
        balances[a] = newBalance;
    }
}

注意,mapping不是数据类型,mapping(address => uint)才是用来声明变量balances的数据类型。

6、数组
solidity中数组可以分为可变数组和不可变数组。

1)数组的创建
contract C {
    uint [5] T = [1,2,3,4,5]; // 不可变数组,数组的长度为5,数组里面的存储的值的类型为uint类型
    uint [] T = [1,2,3,4,5];  // 可变数组,因为总共只有5个数字,所以数组长度暂时为5
}
2)数组长度的更改:T.length
function setTLength(uint len) public {
    T.length = len;
}
// 这种方法只可以用于可变数组,不可变数组的长度是不可以更改的
// 与JavaScript相比,这里没有length()方法,而是length属性
3)添加元素的方法:push()
function pushUintToT() public {        
    T.push(6);
}
// 同样的,该方法只用于可变数组,不可变数组不能使用该方法添加元素
4)创建memory数组
创建一个长度为length的memory类型的数组可以通过new关键字来创建。memory数组一旦创建,它不可通过length修改其长度。
contract C {
    function f(uint len) {
        uint[] memory a = new uint[](len);
        a[6] = 8;
    }
}
5)数组字面量 Array Literals / 内联数组 Inline Arrays
数组字面量,是指以表达式方式隐式声明一个数组,并作为一个数组变量使用的方式。下面是一个简单的例子:
contract C {
    function f() {
        // g([1,2,3]) 报错:传入的[1,2,3]是uint8的,按照声明时的要求必须是uint的,所以对第一个元素进行强制转换
        g([uint(1), 2, 3]);  // second step:调用g()函数
    }
    function g(uint[3] _data) { // first step:这里隐式声明了一个uint[3]类型的数组,memory的、定长的
        // ...
    }
}

通过数组字面量,创建的数组是memory的,同时还是定长的。

元素类型则是使用刚好能存储的元素的能用类型,比如代码里的[1, 2, 3],只需要uint8即可存储。由于g()方法的参数需要的是uint(默认的uint表示的其实是uint256),所以要使用uint(1)来进行类型转换。

还需注意的一点是,定长数组,不能与变长数组相互赋值,我们来看下面的代码:

contract C {
    function f() {
        // The next line creates a type error because uint[3] memory cannot be converted to uint[] memory.
        uint[] x = [uint(1), 3, 4];
}

7、字符串、动态字节数组和固定字节数组及三者相互转换
1)字符串

string a = 'alkjljakf'

string 是一个动态尺寸的UTF-8编码字符串,它其实是一个特殊的可变字节数组;
Solidity中的字符串不像JavaScript中那样,没有length()方法,必须转换成字节数组才能使用length属性来获得长度;

2)固定大小字节数组(Fixed-size byte arrays)

contract C {
    // 0x6c697975656368756e
​
    byte public a = 0x6c; // 0110 1100    1个字节8位
    bytes1 public b = 0x6c; // 0110 1100    1个字节8位
    bytes2 public c = 0x6c69; // 0110 1100 0110 1001    2个字节16位
    bytes3 public d = 0x6c6979; // 0110 1100 0110 1001 0111 1001    3个字节24位
    // ...
    bytes8 public f = 0x6c69797565636875; // 0110 1100 0110 1001 0111 1001 0111 0101 0110 0101 0110 0011 0110 1000 0111 0101
    bytes9 public g = 0x6c697975656368756e; // // 0110 1100 0110 1001 0111 1001 0111 0101 0110 0101 0110 0011 0110 1000 0111 0101 0110 1110
​
}


固定大小字节数组可以通过 bytes1, bytes2, bytes3, …, bytes32来进行声明。PS:byte的别名就是 byte1。
length返回字节数组的元素个数
固定字节数组元素和长度均不能更改,普通定长数组长度不可改,但元素可以依靠索引来更改

3)动态大小字节数组(Dynamically-sized byte array)

string public name = "liyuechun";
​
bytes public g = 0x6c697975656368756e;
// 初始化一个两个字节空间的字节数组
bytes public name = new bytes(2);

bytes 动态字节数组,引用类型。
根据经验,在我们不确定字节数据大小的情况下,我们可以使用string或者bytes;
如果我们清楚的知道或者能够将字节书控制在bytes1 ~ bytes32,那么我们就使用bytes1 ~ bytes32,这样的话能够降低存储成本。
bytes类型可以使用push()方法,具有length属性

4)字符串、动态字节数组和固定字节数组及三者相互转换

string public name = "strange"
​
################################################
#############    string转bytes   ###############
################################################
function nameBytes() constant returns (bytes) {        
    return bytes(name);  // 强制转换string为bytes
}
    
function nameLength() constant returns (uint) {        
    return bytes(name).length;  //  
}
    
function setNameFirstByteForL(bytes1 z) {        
    // 0x4c => "L"
    bytes(name)[0] = z;
}
​
################################################
#####  bytes1~bytes32固定字节数组之间相互转换  #####
################################################
bytes9 name9 = 0x6c697975656368756e;
bytes1(name9);
bytes2(name9);
bytes32(name9);
​
当bytes9转bytes1或者bytes2时,会进行低位截断,0x6c697975656368756e转换为bytes1,结果为0x6c,转换为bytes2时结果为0x6c69。
当bytes9转换为bytes32时会进行低位补齐,结果为0x6c697975656368756e0000000000000000000000000000000000000000000000。
​
################################################
#####   固定字节数组转bytes动态大小字节数组    ######
################################################
bytes9 name9 = 0x6c697975656368756e;
​
// 直接转换报错:固定大小字节数组和动态大小字节数组之间不能简单直接转换
bytes(name9);  
​
// 固定大小字节数组转动态大小字节数组正确的姿势
bytes memory names = new bytes(name9.length);  
for(uint i = 0; i < name9.length; i++) {
    names[i] = name9[i];
}
​
################################################
#######       动态大小字节数组转string      #######
################################################
bytes names = new bytes(2);
function C() {        
    names[0] = 0x6c;
    names[1] = 0x69;
}
string(names)
​
################################################
#####   固定大小字节数组不能直接转换为string    #####
################################################
因为string是特殊的动态字节数组,所以string只能和动态大小字节数组(Dynamically-sized byte array)之间进行转换;
固定大小字节数组要转换为String,必须先转换成动态大小字节数组,再转换为string。
​
有时候会因为字节问题,转换成字符串后会有很多多余的字符u0000,所以:
​
// 完整的固定大小字节数组转string的代码
​
contract C {
    
    function bytes32ToString(bytes32 x) constant returns (string) {
        bytes memory bytesString = new bytes(32);
        uint charCount = 0;
        for (uint j = 0; j < 32; j++) {
            byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
            if (char != 0) {
                bytesString[charCount] = char;
                charCount++;
            }
        }
        bytes memory bytesStringTrimmed = new bytes(charCount);
        for (j = 0; j < charCount; j++) {
            bytesStringTrimmed[j] = bytesString[j];
        }
        return string(bytesStringTrimmed);
    }
​
    function bytes32ArrayToString(bytes32[] data) constant returns (string) {
        bytes memory bytesString = new bytes(data.length * 32);
        uint urlLength;
        for (uint i = 0; i< data.length; i++) {
            for (uint j = 0; j < 32; j++) {
                byte char = byte(bytes32(uint(data[i]) * 2 ** (8 * j)));
                if (char != 0) {
                    bytesString[urlLength] = char;
                    urlLength += 1;
                }
            }
        }
        bytes memory bytesStringTrimmed = new bytes(urlLength);
        for (i = 0; i < urlLength; i++) {
            bytesStringTrimmed[i] = bytesString[i];
        }
        return string(bytesStringTrimmed);
    }    
}

猜你喜欢

转载自blog.csdn.net/lj900911/article/details/83037625