Solidity变量的声明及作用域(十五)|入门系列

2017/5/7 posted in  Solidity入门系列

Solidity中的作用域规则同于Javascript。1

1. 声明及默认值

变量声明后均有一个初值,是对应类型的“零态”,意即对应的类型的字节表示的全0。使用中需要特别小心的是这与其它语言的默认值如nullundefined有所不同,因为有时0也是一种业务值。下面我们来看一些常见类型的声明,以及它们的默认值:

pragma solidity ^0.4.0;

contract DeclareOfElement{
  function f() returns (bool, uint, address, bytes32, bytes, string, uint8[]){
        bool b;//fasle
        uint i;//0
        address addr;//0x0
        bytes32 by;//0x0000000000000000000000000000000000000000000000000000000000000000

        bytes  memory varBy;//0x
        string memory str;//
        uint8[] memory arr;//

        return (b, i, addr, by, varBy, str, arr);
    }
}

在上述例子中,bool的默认值为falsebytes32的默认值为32字节长的0。对于引用类型,bytes类型默认值为空字节数组,string为默认值为空串,动态数组uint8[] memory arr为空。

2. 引用类型的初始化

对于值类型,声明变量后,即赋值为默认值,可正常使用。而对于引用类型是否仍需同其它语言一样进行显式初始化,进行内存分配,才能进一步使用呢,我们来分别看一下。

2.1 动态数组

对于数组,声明后,仍需分配内存后方可访问,下面的代码会报错Exception during execution. (invalid opcode)

pragma solidity ^0.4.0;

contract initial{
  function f() returns (bytes1, uint8){
    bytes memory bs;
    uint8[] memory arr;

    return (bs[0], arr[0]);
  }
}

由于上例中,我们越界访问元素,故抛出了异常。如果要主动分配内存,进行初始化,如何做呢,一起来看看下面的实现。

pragma solidity ^0.4.0;

contract ArrayInitialOk{
  function f() returns (bytes1, uint8){
    bytes memory bs = new bytes(1);
    uint8[] memory arr = new uint8[](1);

    return (bs[0], arr[0]);
  }
}

上述代码通过new关键进行了内存分配,现在即可正常访问第一个数组元素了。

2.2 映射

映射的声明后,不用显式初始化即可使用,只是里面不会有任何值,下面是一个非常简单的映射的例子。

pragma solidity ^0.4.0;

contract DeclareOfMapping{
  mapping(uint => string) m;

  function f() returns (string){
    return m[0];
  }
}

上面的例子中,我们定义了一个映射,然后返回了映射中其中的一个元素值。由于没有存值,故这里将返回的是空串。

2.3 枚举

枚举类型不用显式初始化,默认值将为0。即顺位第一个值。下面来看一个枚举的示例。

pragma solidity ^0.4.0;

contract DeclareOfEnum{
  enum Light{RED, GREEN, YELLOW}

  Light light;

  function f() returns (Light){
    return light;
  }
}

上面的代码的输出结果为0

2.4 结构体

结构体声明后,不用显式初始化即可使用。当没有显式初始化时,其成员值均为默认值。一起来看一个结构体声明的例子:

pragma solidity ^0.4.0;

contract DeclareOfStruct{
  struct S{
    uint a;
    string b;
    bytes c;
  }

  S s;

  function f() returns(uint, string, bytes){
    return (s.a, s.b, s.c);
  }
}

上面的代码中定义了一个结构体,并创建了一个变量S s。所有结构体内的成员值均被赋值为了默认值,上述代码运行后,将返回0,空串,0x

2.5 delete操作符

使用delete操作符会将对象重置为默认值2

3. 作用域

变量无论在函数内什么位置定义,其作用域均为整个函数,而非大多数据语言常见的块级作用域。下面的例子会报错Identifier already declared

pragma solidity ^0.4.0;

contract ScopeErr{
  function f(){
    { uint8 a = 0;}
    
    //Identifier already declared
    //{ uint8 a = 1;}
  }
}

上例中,由于变量作用域是函数作用域。故上述uint8 a这个变量声明了两次,故会报错Identifier already declared

我们来看一个稍微隐蔽点的例子:

pragma solidity ^0.4.0;

contract FunctionScope{
  function f() returns (uint8){
    for(var i = 0; i < 10; i++){
      //do sth
    }
    return i;
  }
}

读者可以试想一下上述代码能否编译通过呢。

实际上是可以的,而且可以返回值10。原因就是因为i变量虽然是在for循环中被定义,但它的的作用域仍是整个函数,所以在函数内的任何位置均可以访问到这个变量。

我们再来看一个更加极端的例子。

pragma solidity ^0.4.0;

contract FunctionScope2{
  function f() returns (uint8){
    if(false){
      uint8 foo = 10;
    }
    return foo;
  }
}

上述的代码中,虽然变量的声明看似没有执行到,但是由于变量作用域是整个函数,且由于第一部分讲到,任何变量声明后均有其默认值。故上例中将返回默认值0

关于作者

专注基于以太坊(Ethereum)的相关区块链(Blockchain)技术,了解以太坊,Solidity,Truffle,web3.js。

个人博客: http://tryblockchain.org
版权所有,转载注明出处

参考资料

处于某些特定的环境下,可以看到评论框,欢迎留言交流^_^。

友情链接: 区块链技术中文社区    深入浅出区块链