3 类型系统与运算符

2019-11-19  本文已影响0人  智能合约大师兄

3.1 类型系统概述

对任何一种编程语言来说类型系统都是组成后续复杂功能模块的基础和核心。现在大部分的动态语言都通过不指定变量明确类型而通过编译器来进行表达式自动推导从而确定变量类型来简化编程和减少错误。solidity虽然也部分支持动态类型推导来简化编程,但作为一种静态编译语言笔者还是建议明确指定变量类型。这样能充分利用编译工具的提供的错误检查机制提前发现潜在的错误。因为不同于传统的编程语言和运行平台,智能合约一旦出错可能涉及更大的经济方面的损失。从小处着手尽量避免潜在错误发生的可能性总是有额外有益处的。

Solidity内置的变量类型主要分两类:值类型和引用类型。值类型变量是指在进行参数传递时如变量赋值或函数调用传参,总是进行值拷贝新旧变量之间是相互独立的,彼此的值修改不影响另外一个变量。而与此相反引用变量类型一般是复杂类型变量如自定义变量和各种类型数组等,占用空间比较大如进行值拷贝效率低下所以都是通过变量引用进行参数传递,也就是新旧变量都指向同一个存储空间彼此对值的修改都会互相影响。

Solidity中常见变量分类如下示:


image.png

需要特别注意的是solidity中函数本身也是一种类型可以进行变量间的赋值。也就是solidity也支持类似函数指针的概念,关于函数相关内容后续会有专门章节进行讨论读者可以先暂时忽略。

3.1.1 变量声明

在任何一种编程语言中标识符定义都是最基础的单元。变量名、函数名以及合约名称等都需要通过有效的标识符进行定义。笔者认为对语言组成结构进行文字化描述总是会有理解偏差或表达不准确的情况存在,因此本书在进行solidity语言组成结构描述时,引入了solidity语言官方正式的文法定义,然后再辅以文字化的解释和描述,读者在阅读时以文法描述为第一标准。标识符的文法定义如下:

Identifier = [a-zA-Z_$][a-zA-Z_$0-9]*

有效的标识符组成规则如下:以字母、下划线或美元符号开头后跟0个或多个字母、下划线、美元符号或数字。

需要进行额外说明的是语言的文法定义其本质就是正则表达式的递归应用。如果读者接触过类似LEX词法分析工具和YACC语法分析工具,对这种正式的语言文法定义就会比较熟悉和亲切了。这两者配合号称生成编译器的编译器,在实现自定义语法规则的解释脚本系统时相当方便和强大。自定义的标识符不能和系统关键字冲突,如下都是有效的标识符:

uint _aAA111aa;
uint $bB00Bbb;
uint ccAc_$$_a1123Caa;

定义变量时除了需要有效的标识符还需要对变量类型进行说明,包括基本的内置类型和复杂类型。其中基本类型包括地址、布尔、字符串、整数、字节、小数(也被称之为fixed目前只有语法意义还不被实际支持,本书主要面向工程实践故fixed相关内容不做讨论)以及自动类型推导var。复杂类型包括映射、各种类型数组、用户自定义类型以及函数指针类型。变量类型的正式文法定义如下示:

TypeName = ElementaryTypeName
 | UserDefinedTypeName
 | Mapping
 | ArrayTypeName
 | FunctionTypeName

关于变量类型更详细的介绍在讲解具体变量类型时会深入讨论,这里先对整体有个印象即可。与变量定义有关的另外一个重要内容是其存储位置指示符,主要分临时存储(临时变量和函数参数等)和永久存储(合约状态变量),文法定义如下示:

StorageLocation = 'memory' | ‘storage'

有了前面关于变量类型、标识符定义以及储存位置约定的准备知识,变量定义语句其实就比较好理解了,正式文法定义如下:

VariableDeclaration = TypeName StorageLocation? Identifier

有效的变量定义语句格式如下:以变量类型开头后跟可选的储存位置限定符最后是有效的标识符。

3.1.2 操作符

solidity也支持其它编程语言常见类型的操作符,包括算术运算+、-、*、/和%等,逻辑运算 ||、&&及!等,位运算以及比较运算等,还支持常见的 “?:”三目运算符,其中逻辑运算符也遵循短路操作原则。关于操作符没多少新鲜的知识,可以直接看其文法定义:

Expression = Expression ('++' | '--')
 | NewExpression | IndexAccess | MemberAccess
 | FunctionCall | '(' Expression ')'
 | ('!' | '~' | 'delete' | '++' | '--' | '+' | '-') Expression
 | Expression ('*' | '/' | '%' | '+' | '-' | '**') Expression
 | Expression ('&' | '^' | '|' | '<<' | '>>') Expression
 | Expression ('<' | '>' | '<=' | '>=') Expression
 | Expression ('==' | '!=' | '&&' | '||') Expression
 | Expression '?' Expression ':' Expression
 | Expression ('=' | '|=' | '^=' | '&=' | '<<=' | '>>=' |
 '+=' | '-=' | '*=' | '/=' | '%=') Expression

由定义可知常见的变量与操作符和赋值运算合并的左值运算也是支持的,如下都是合法的语句:

function test() public pure returns(uint){
 uint a = 1;
 a++;
 ++a;
 a += 2;
 a /= 3;
 return a;
}

不同类型的变量其适用的操作符不同,这点在具体变量类型说明时会有详细介绍。至于操作符的优先级也遵循我们传统编程语言的优先级定义,不过笔者建议忘掉操作符的具体优先级顺序,在需要特定优先级的地方适用括号运算符来人为指定来,减少隐含BUG可能性同时提高代码可读性。类型的正式文法定义如下:

ElementaryTypeName = 'address' | 'bool' | 'string' | 
‘var' | Int | Uint | Byte                  
Int = 'int' | 'intN' (8 <= N <= 256 && N%8 == 0)
Uint = 'uint' | 'uintN' (8 <= N <= 256 && N%8 == 0)
Byte = 'byte' | 'bytes' | 'bytesN' (1 <= N <= 32)

其中int是int256是简写,uint是uint256简写。字节数组长度最低为1,最高为32步长为1。

3.2 基本类型变量

上一篇下一篇

猜你喜欢

热点阅读