一起学智能合约之二基本类型

  • Post author:
  • Post category:其他

 

一起学智能合约之二基本类型

首先需要说明的是,如果大家对下面的某些点存疑,可去官网查询,以官网的文章为准。

地址:

英文:https://solidity.readthedocs.io/en/v0.4.24/

中文:http://solidity-cn.readthedocs.io/zh/develop/index.html

源码:

https://github.com/ethereum/solidity

Solidity语言不可免俗的是把数据类型分成了两类:值类型和引用类型。当然,它也还有一些其它的类型。

  • 值类型

值类型指的是在使用过程,总是要在内存中生成>=1个相同的拷贝。值类型不会产生数据异常问题,因为它自己维护着一个拷贝。

它主要包含以下几种:

 

  1. 布尔类型(Booleans)

这个比较简单,只有true 和false两个状态。

它支持的运行符操作:!(逻辑非),&&(逻辑与),||(逻辑或),==(相等),!=(不相等)。处理它们时和主流的语言的基本一致。

 

  1. 整型(Integers)

无符号:uint8,uint16……..uint256  以8位步进

有符号:int8,int16…….int256      以8位步进

这里有一个需要说明的,在golang调用智能合约中int(int256)时,直接传入int64 是不行的,要转换成big.Int。

默认uint int均为uint256,int256.

它支持的运算符包括:

比较运算符: <= , < , == , != , >= , > (返回布尔值)

位运算符: & , | , ^ (异或), ~ (位取反)

算数运算符: + , – , 一元运算 – , 一元运算 + , * , / , % (取余) , ** (幂), << (按位左移) , >> (按位右移)

 

  1. 定长浮点型(Fixed Point Numbers)

定长浮点型以太坊支持的还是不太好的,所以这部分可以暂时略过,有兴趣的可以去上面提供的网址学习。

包括:fixed/unfixed(fixed128x19 /ufixed128x19)

 

  1. 地址类型(Address)

address:地址类型存储一个 20 字节的值(以太坊地址的大小)。 地址类型也有成员变量,并作为所有合约的基础。

它包括以下变量:

balance:地址上的余额

transfer:向地址上发送以太币

send:transfer的低级实现。如果执行失败,当前合约不会终止,但会返回false。

下面的三个都是低级函数,可以在成不得已的时候使用,但应该知道,它破坏了Solitidy的类型安全性。

call:二进制接口的合约交互调用。如果正常完成返回true,否则返回false。注意,它这里不准使用简写比如unit256不能写uint,虽然二者是一回事。

callcode:类似call,但要保证变量顺序一致。

delegatecall:类似call

它们三个区别在于:callcode和Call一致,只是将被调用函数搬到调用者的环境里执行(是不类似于c++中的调用约定)。delegatecall和callcode相同,只是操作的Sender不同。比如

A调用(callcode)B调用(callcode)C,C中看到的msg.sender为B。

A调用(callcode)B调用(delegatecall)C,C中看到的msg.sender为A。

 

  1. 定长字节数组(Fixed-size byte arrays)

bytes1,bytes2,bytes3……bytes32

byte 等价于bytes1

支持的运算符有:

比较运算符:<=, <, ==, !=, >=, > (返回布尔型)

位运算符: &, |, ^ (按位异或), ~ (按位取反), << (左移位), >> (右移位)索引访问:如果 x 是 bytesI 类型,那么 x[k] (其中 0 <= k < I)返回第 k 个字节(只读)。

 

  1. 变长字节数组

主要有两类:

bytes:变长字节数组,其实就是个数组,它不能算做值类型。

string:变长的utf-8编码字符串类型,数组,同样不是值类型。

  1. 地址常量(Address Literals)

类似 0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF 这样的通过了地址校验和测试的十六进制字面常数属于 address 类型。 长度在 39 到 41 个数字的,没有通过校验和测试而产生了一个警告的十六进制字面常数视为正常的有理数字面常数。

8、有理数和整数字面常量(Rational and Integer Literals)

其实就是十进制(没有八进制)、科学符号

9、字符串字面常量(String literals)

这个比较好理解,就是用引号包裹起来的字符串:”example” ‘big’,支持转义字符。

10、十六进制字面常数(Hexadecimal literals)

十六进制字面常数以关键字 hex 打头,后面紧跟着用单引号或双引号引起来的字符串(例如,hex”001122FF”)。 字符串的内容必须是一个十六进制的字符串,它们的值将使用二进制表示。

十六进制字面常数跟字符串字面常数很类似,具有相同的转换规则。

11、枚举(Enums)

看一下官方的例子:

enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }

说明中提到自动会转换为uint8. 函数类型是一种表示函数的类型。

 

12、函数类型(Function Types)

以下为官网说明:

可以将一个函数赋值给另一个函数类型的变量,也可以将一个函数作为参数进行传递,还能在函数调用中返回函数类型变量。 函数类型有两类:- 内部(internal) 函数和 外部(external) 函数:

内部函数只能在当前合约内被调用(更具体来说,在当前代码块内,包括内部库函数和继承的函数中),因为它们不能在当前合约上下文的外部被执行。 调用一个内部函数是通过跳转到它的入口标签来实现的,就像在当前合约的内部调用一个函数。

外部函数由一个地址和一个函数签名组成,可以通过外部函数调用传递或者返回。

函数类型表示成如下的形式:

function (<parameter types>) {internal|external} [pure|constant|view|payable] [returns (<return types>)]

与参数类型相反,返回类型不能为空 —— 如果函数类型不需要返回,则需要删除整个 returns (<return types>) 部分。

函数类型默认是内部函数,因此不需要声明 internal 关键字。 与此相反的是,合约中的函数本身默认是 public 的,只有当它被当做类型名称时,默认才是内部函数。

有两种方法可以访问当前合约中的函数:一种是直接使用它的名字,f ,另一种是使用 this.f 。 前者适用于内部函数,后者适用于外部函数。

如果当函数类型的变量还没有初始化时就调用它的话会引发一个异常。 如果在一个函数被 delete 之后调用它也会发生相同的情况。

如果外部函数类型在 Solidity 的上下文环境以外的地方使用,它们会被视为 function 类型。 该类型将函数地址紧跟其函数标识一起编码为一个 bytes24 类型。。

请注意,当前合约的 public 函数既可以被当作内部函数也可以被当作外部函数使用。 如果想将一个函数当作内部函数使用,就用 f 调用,如果想将其当作外部函数,使用 this.f 。

除此之外,public(或 external)函数也有一个特殊的成员变量称作 selector,可以返回 ABI 函数选择器.

其实就是一个函数指针。千变万化也就是多了些限制。

 

  • 引用类型

引用类型指的是在使用时,内存中只维护一个拷贝,其它使用都是它的一个指针,如果没学过编程的或者没学过指针的可以认为它是一个计数器,表明在使用这个唯一的内存拷贝。这里需要注意的是,引用类型如果别的函数修改了这个值,引用的数据是会发生变化的,一定要引起注意。

在智能合约中,每个字节都是钱,所以对大于256位的数据类型,会有一个额外属性,叫做“数据位置”,其实就是说明这个数据保存在内存还是存储中。

默认是memory,局部变量是srorage,状态变量的数据位置强制是sgorage.下来其实就是对应开始说的了,两两赋值时它们会不会创建独立拷贝。

只有从状态变量到局部变量使用引用(当然,从一个引用到另外一个引用赋值也不会创建拷贝),其它存储到内存,存储到状态都要创建拷贝。

1、数组

Solidity中的数组可以在声明时指定长度,也可以动态调整大小。

存储的数组:元素类型可以是任意的(即元素也可以是数组类型,映射类型或者结构体)。 内存的数组:元素类型不能是映射类型,如果作为 public 函数的参数,它只能是 ABI 类型。

举个例子:

uint256[5]:表示5个长度的uint256数组。

uint16[]:表示uint16的动态数组

同样它也支持多维数组:

 uint8[2][3]:表示有3个uint8的长度为2的数组。

 uint8[][3]:动态数组的数组。需要注意的,它的数组声明与其它语言的相关。

bytes 和 string 类型在上文中提到过他们是数组,它们在内存中是连续排在一起的,而不是按字节排在一起(类似于c++中的内存字节对齐,但有些不同)。

创建内存数组可以使用new关键字:

bytes memeory b = new bytes(len);

 

2、结构体

发现好多新的语言都没有类,只有结构体,但细看结构体,又比传统的结构体功能强大,Solidity也是如此。

看一个简单的例子:

struct Example{

         uint256 num;

         bool ok;

}

 

三、映射

映射类型可以和c++或者其它语言中的map或者hashmap相比较,它的应用形式如下:

mapping(_KeyType => _ValueType)。

_KeyType :除映射、变长数组、合约、枚举以及结构体以外的几乎所有类型。

_ValueType:任何类型。

在映射中,存储 key其实是存储它的 keccak256 哈希值。

因此映射是没有长度的,只有状态变量(或者在 internal 函数中的对于存储变量的引用)可以使用映射类型。

可以将映射声明为 public,然后来让 Solidity 创建一个 getter。 _KeyType 将成为 getter 的必须参数,并且 getter 会返回 _ValueType。

_ValueType 也可以是一个映射。这时在使用 getter 时将将需要递归地传入每个 _KeyType 参数。

看下面的例子:

    mapping(address => uint) public balances;

 

    function Address(uint balance) public {

        balances[msg.sender] = balance;

    }

 

四、类型转换

1、显式转换

强制指定转换成的类型,转换过程中注意数据的舍弃。

int8 x = 6;

uint y = uint(x)

 

2、隐式转换

当对两个不同的类型进行运算时,编译器会隐式的将二者进行适配,也就是隐式转换。前提是在转换语义上可行。比如 uint8 可以转换成 uint16,int128 转换成 int256,但 int8 不能转换成 uint256。

五、delete

这个重点说一下,它有点类似于reset,删除旧的重新给新的默认值,但是它处理映射时要小心。


版权声明:本文为fpcc原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。