• 智能合约语言 Solidity 教程系列(1)


    写在前面

    现在可以阅读完整的、有社区大神们翻译的 Solidity 中文文档

    类型

    Solidity 是一种静态类型语言,意味着每个变量(本地或状态变量)需要在编译时指定变量的类型(或至少可以推倒出类型)。Solidity 提供了一些基本类型可以用来组合成复杂类型。

    Solidity 类型分为两类:

    • 值类型(Value Type) - 变量在赋值或传参时,总是进行值拷贝。
    • 引用类型(Reference Types)

    值类型(Value Type)

    值类型包含:

    • 布尔类型(Booleans)
    • 整型(Integers)
    • 定长浮点型(Fixed Point Numbers)
    • 定长字节数组(Fixed-size byte arrays)
    • 有理数和整型常量(Rational and Integer Literals)
    • 字符串常量(String literals)
    • 十六进制常量(Hexadecimal literals)
    • 枚举(Enums)
    • 函数类型(Function Types)
    • 地址类型(Address)
    • 地址常量(Address Literals)

    函数类型及地址类型(Address)有单独的博文,查看后续的说明。

    布尔类型(Booleans)

    布尔(bool):可能的取值为常量值 true 和 false。

    布尔类型支持的运算符有:

    • !逻辑非
    • && 逻辑与
    • || 逻辑或
    • == 等于
    • != 不等于

    注意:运算符**&&和 ||**是短路运算符,如 f(x)||g(y),当 f(x)为真时,则不会继续执行 g(y)。

    整型(Integers)

    int/uint: 表示有符号和无符号不同位数整数。支持关键字 uint8 到 uint256 (以 8 步进),
    uint 和 int 对应的是 uint256 和 int256。

    支持的运算符:

    • 比较运算符: <=, < , ==, !=, >=, > (返回布尔值:true 或 false)
    • 位操作符: &,|,^(异或),~(位取反)
    • 移位运算: << (左移位), >>(右移位)
    • 算术操作符:+,-,一元运算-,一元运算 +,*,/, %(取余数), **(幂)

    说明:

    1. 整数除法总是截断的,但如果运算符是字面量(字面量稍后讲),则不会截断。
    2. 整数除 0 会抛异常。
    3. 移位运算的结果的正负取决于操作符左边的数。x << y 和 x * (2**y) 是相等, x >> y 和 x / (2**y) 是相等的。
    4. 不能进行负移位,即操作符右边的数不可以为负数,否则会抛出运行时异常。

    每个整数变量,都是有限定取值范围的,如 uint32 的取值分为是 0 到 2**32 - 1, 如果运算之后,取值不在这个范围内,结果会被截断(溢出)。

    注意:Solidity 中,右移位是和除等价的,因此右移位一个负数,向下取整时会为 0,而不像其他语言里为无限负小数。

    定长浮点型(Fixed Point Numbers)

    注意:定长浮点型 Solidity(发文时)还不完全支持,它可以用来声明变量,但不可以用来赋值。

    fixed/ufixed: 表示有符号和无符号的固定位浮点数。关键字为 ufixedMxN 和 ufixedMxN。
    M 表示这个类型要占用的位数,以 8 步进,可为 8 到 256 位。
    N 表示小数点的个数,可为 0 到 80 之间

    支持的运算符:

    • 比较运算符: <=, < , ==, !=, >=, > (返回布尔值:true 或 false)
    • 算术操作符:+,-,一元运算-,一元运算 +,*,/, %(取余数)
      注意:它和大多数语言的 float 和 double 不一样, M 是表示整个数占用的固定位数,包含整数部分和小数部分。因此用一个小位数(M 较小)来表示一个浮点数时,小数部分会几乎占用整个空间。

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

    关键字有:bytes1, bytes2, bytes3, ..., bytes32。(以步长 1 递增)
    byte 代表 bytes1。

    支持的运算符:

    • 比较符: <=, <, ==, !=, >=, > (返回 bool)
    • 位操作符: &, |, ^ (按位异或),~(按位取反), << (左移位), >> (右移位)
    • 索引(下标)访问: 如果 x 是 bytesI,当 0 <= k < I ,则 x[k]返回第 k 个字节(只读)。

    移位运算和整数类似,移位运算的结果的正负取决于操作符左边的数,且不能进行负移位。
    如可以-5<<1, 不可以 5<<-1

    成员变量:
    .length:表示这个字节数组的长度(只读)。

    补充小技巧: 如何在 Remix 中把内容传递给函数的定长字节数组参数,答案是使用 16 进制形式(0x1122)传递。
    如果参数是定长字节数组的数组 bytes32[],则在 Remix 中参数内容传递形式为:["0x00","0x0a"]。

    变长(动态分配大小)字节数组(Dynamically-sized byte array)

    • bytes:动态分配大小字节数组, 参见 Arrays,不是值类型!
    • string:动态分配大小 UTF8 编码的字符类型,参看 Arrays。不是值类型!

    根据经验:
    bytes 用来存储任意长度的字节数据,string 用来存储任意长度的(UTF-8 编码)的字符串数据。
    如果长度可以确定,尽量使用定长的如 byte1 到 byte32 中的一个,因为这样更省空间。

    有理数和整型常量(Rational and Integer Literals)

    也有人把 Literals 翻译为字面量

    整型常量是有一系列 0-9 的数字组成,10 进制表示,比如:8 进制是不存在的,前置 0 在 Solidity 中是无效的。

    10 进制小数常量(Decimal fraction literals)带了一个**., 在.**的两边至少有一个数字,有效的表示如:1., .1 和 1.3.

    科学符号也支持,基数可以是小数,指数必须是整数, 有效的表示如: 2e10, -2e10, 2e-10, 2.5e1。

    数字常量表达式本身支持任意精度,也就是可以不会运算溢出,或除法截断。但当它被转换成对应的非常量类型,或者将他们与非常量进行运算,则不能保证精度了。
    如:(2*100 + 1) - 2*100 的结果为 1(uint8 整类) ,尽管中间结果已经超过计算机字长。另外:0.5 * 10 的结果是 5,尽管有非整形参与了运算。

    只要操作数是整形,整型支持的运算符都适用于整型常量表达式。
    如果两个操作数是小数,则不允许进行位运算,指数也不能是小数。

    注意:
    Solidity 对每一个有理数都有一个数值常量类型。整数常量和有理数常量从属于数字常量。所有的数字常表达式的结果都属于数字常量。所以 1 + 2 和 2 + 1 都属于同样的有理数的数字常量 3

    警告:
    整数常量除法,在早期的版本中是被截断的,但现在可以被转为有理数了,如 5/2 的值为 2.5

    注意:
    数字常量表达式,一旦其中含有常量表达式,它就会被转为一个非常量类型。下面代码中表达式的结果将会被认为是一个有理数:

    uint128 a = 1;
    uint128 b = 2.5 + a + 0.5;

    字符串常量

    字符串常量是指由单引号,或双引号引起来的字符串 ("foo" or 'bar')。字符串并不像 C 语言,包含结束符,"foo"这个字符串大小仅为三个字节。和整数常量一样,字符串的长度类型可以是变长的。字符串可以隐式的转换为 byte1,...byte32 如果适合,也会转为 bytes 或 string。

    字符串常量支持转义字符,比如 ,xNN,uNNNN。其中xNN 表示 16 进制值,最终转换合适的字节。而uNNNN 表示 Unicode 编码值,最终会转换为 UTF8 的序列。

    十六进制常量(Hexadecimal literals)

    十六进制常量,以关键字 hex 打头,后面紧跟用单或双引号包裹的字符串,内容是十六进制字符串,如 hex"001122ff"。
    它的值会用二进制来表示。

    十六进制常量和字符串常量类似,也可以转换为字节数组。

    枚举(Enums)

    在 Solidity 中,枚举可以用来自定义类型。它可以显示的转换与整数进行转换,但不能进行隐式转换。显示的转换会在运行时检查数值范围,如果不匹配,将会引起异常。枚举类型应至少有一名成员。下面是一个枚举的例子:

    pragma solidity ^0.4.18;
    
    contract test {
        enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
        ActionChoices choice;
        ActionChoices constant defaultChoice = ActionChoices.GoStraight;
    
        function setGoStraight() {
            choice = ActionChoices.GoStraight;
        }
    
        // Since enum types are not part of the ABI, the signature of "getChoice"
        // will automatically be changed to "getChoice() returns (uint8)"
        // for all matters external to Solidity. The integer type used is just
        // large enough to hold all enum values, i.e. if you have more values,
        // `uint16` will be used and so on.
        function getChoice() returns (ActionChoices) {
            return choice;
        }
    
        function getDefaultChoice() returns (uint) {
            return uint(defaultChoice);
        }
    }

    代码实例

    下面我们用一个真实的合约代码来理解下各个类型及操作符。

    pragma solidity ^0.4.18;
    contract demo1{
    
        function add(uint a, uint b)   public pure returns(uint){
          return a + b;
        }
        //  two wirte function
        function divide(uint a,uint b)  public pure returns (uint){
           
           return a / b ;
        }
        
        function leftshift(uint a, uint b) public pure returns (uint){
            return a << b;
        }
        
         function rightshift(uint a, uint b)  public pure returns (uint){
            return a >> b;
        }
        
        function interLiteral() public pure returns (uint, uint) {
            return ((2**100 + 1) - 2**100,  0.5*10);
        }
    }
     

      

     我们看到4,8,13,17,21 出现黄色的警告

    下面就分析一下这两个警告信息的原因及解决方法。

    public声明

    第一个警告“No visibility specified. Defaulting to “public”。”,字面直译为:未指定可见性,采用默认public可见范围。

    这个警告是提醒开发者,你未指定当前function的可见范围,合约会默认采用public,有一定的风险存在。特别针对一些不可对外公开访问的智能合约,需要注意此提示。

    消除此警告的方法很简单,只需在方法中添加public声明即可。

    此时,再执行编译操作,刚才针对public的警告信息已经不存在了。借此,再延伸一下智能合约方法的4类可见性指定:


    - public:智能合约外部和内部都可使用的方法;
    - internal:智能合约(包括派生合约)内部可用调用的方法;
    - external:可通过其他合约和交易进行调用的方法;
    - private:只有在定义的合约中才可以调用,即使派生的合约也无法调用;

    pure 

    首先将第二个警告内容直译之后为:功能状态可变性可以限制为pure。这里就引出了pure限制词。在之前的版本中我们经常使用constant来限制一个方法的制度性,当用constant修饰之后,此方法在被调用时不会进行存储的变更,同样不会产生交易和gas花费。而pure正是constant的替代品,逐渐的在替代constant的功能。关注微信公众号“程序新视界”,后面会针对此块进行详细讲解。

    当知道了原因之后,解决方法就变得简单,经过再次改进之后的代码为:

    pragma solidity ^0.4.18;
    contract demo1{
    
        function add(uint a, uint b)   public pure returns(uint){
          return a + b;
        }
        //  two wirte function
        function divide(uint a,uint b)  public pure returns (uint){
           
           return a / b ;
        }
        
        function leftshift(uint a, uint b) public pure returns (uint){
            return a << b;
        }
        
         function rightshift(uint a, uint b)  public pure returns (uint){
            return a >> b;
        }
        
        function interLiteral() public pure returns (uint, uint) {
            return ((2**100 + 1) - 2**100,  0.5*10);
        }
    }

    如上图警告就没有了。

  • 相关阅读:
    Segment Routing之IPv6 SR概述
    ping6 connect: Invalid argument
    glance image-list
    SRv6技术研究和组网设计
    physical_interface_mappings
    bond
    srv6 tools---SRext
    srv6
    8月18号
    8月17号
  • 原文地址:https://www.cnblogs.com/zuolun2017/p/14119537.html
Copyright © 2020-2023  润新知