Solidity中的作用域规则同于Javascript。1
1. 声明及默认值
变量声明后均有一个初值,是对应类型的“零态”,意即对应的类型的字节表示的全0
。使用中需要特别小心的是这与其它语言的默认值如null
或undefined
有所不同,因为有时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
的默认值为false
,bytes32
的默认值为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
版权所有,转载注明出处
参考资料
处于某些特定的环境下,可以看到评论框,欢迎留言交流^_^。