变量类型
Solidity变量类型分为两大类——值类型、引用类型
值类型
- 布尔(bool)
- 整型(int/uint)
- 地址(address)
- 定长字节数组(fixed byte arrays)
- 有理数、整型、字符串常量和十六进制常量
- 枚举类型(enums)
- 函数(function)
引用类型
- 字符串(string)
- 数组(array)
- 结构体(struts)
注意
注意:值传递和引用传递。值类型的变量,赋值是简单的值传递,即两个变量占有独立的存储区域。引用类型赋值传递的是变量的引用,即两个变量指向同一存储区域
值类型——布尔 (bool)
bool: 可能的取值为常量值true和false(默认false)。
支持的运算符:
- ! 逻辑非
- && 逻辑与
- || 逻辑或
- == 等于
- != 不等于
实例:
bool a = true;
bool b = !a;
// a == b -> false
// a != b -> true
// a || b -> true
// a && b -> false
值类型——整型(int/uint)
- int(m):有符号整数
- uint(m):无符号整数
- m关键字取值为8~256步幅是8 ,表示在内存中2进制的位数,控制了整数的取值范围,不写默认为256。
- uint和int分别是uint256和int256的别名。
- m一定要是8的整数倍,数的范围无符号:
0/2^{m}-10/2m−1
有符号:
-2^{m-1}/2^{m-1}-1−2m−1/2m−1−1
操作
比较:<=,<,==,!=,>=,>(结果为bool)
位操作符:&,|,^(按位异或),~(按位取反)
算术运算符:+, - ,一元 - ,一元 +,*,/,%(取余),**(幂),<<(左移),>>(右移)
注意
- 除法总是截断,但如果两个运算符都是常量(或常量表达式),则它不会截断。
- 除零和取余有零引发运行时异常。
- 在Solidity中不支持八进制。
值类型——定点数(Fixed Point Numbers)
定点小数
到现在为止还没有被solidity完全支持,可以被声明,不能被赋值也不能用定点数赋值fixed/ufixed 各种大小的有符号和无符号定点小数,ufixedMxN and fixedMxN关键字M代表定点数占用的二进制位数,N代表定点数能表示多少位小数,M必须是8-256之间的,以8为步幅的整数,N必须是0-80之间的整数,ufixed 和fixed 默认为ufixed128x18和fixed128x18
- 比较运算: <=, <, ==, !=, >=, > (结果为bool)
- 算数运算: +, -, 一元-, 一元 +, *, /, % 取余
值类型——地址类型(address)
地址:保存一个20字节值(40位16进制数)。地址类型也有成员,并作为所有合约的基础。
地址成员:
- 以wei 位单位返回该地址的余额
<address>.balance (uint256)
- 从当前合约地址中给调用函数的地址账户转入amounts数量(以wei为单位)的金额,如果执行失败,将抛出异常,需要支付2300gas的费用,不可以调整。
<address>.transfer(uint256 amount)
范例:
pragma solidity ^0.4.0;
contract AddressExample {
function AddressExample() payable{}
function giveEthersTo(address _toAccount,uint amount){
if (this.balance >=amount){
_toAccount.transfer(amount);
}
}
function getBalance() view returns(uint){
return this.balance;
}
}
- send是低级对等的转账。执行失败,不会抛出异常,会返回false,需要支付2300gas的费用,不可以调整。推荐使用transfer而不使用send
<address>.send(uint256 amount) returns (bool)
- call通过底层调用另一个合约的函数。
<address>.call(...) returns (bool)
参数支持任何类型任意数量。每个参数会按规则被打包成32字节并一一拼接到一起。
call()函数支持ABI协议定义的函数选择器。如果第一个参数恰好4个字节,在这种情况下,第一个参数会被认为是指定的函数签名。所以如果你只是想发送消息体,需要避免第一个参数是4个字节。
pragma solidity ^0.4.0;
contract Person{
uint public age = 10;
uint public num ;
string public name;
bytes public data;
function increaseAge( uint _num,string _name) returns (uint){
num = _num;
name = _name;
data = msg.data;
return ++age;
}
function(){
data = msg.data;
age--;
}
}
contract CallTest{
bytes4 public methodId;
function callByFun(address addr)returns (bool){
methodId = bytes4(keccak256("increaseAge(uint256,string)"));
return addr.call(methodId, 1,"abc");
}
}
call函数返回一个bool值,以表明执行成功还是失败。正常结束返回true,异常终止返回false。
可以通过.gas()函数调整支付gas的数量
addr.call.gas(1000000)(1, "MyName");
可以通过.value()函数控制支付的以太币的数量
addr.call.value(1 ether)(1, "MyName");
这两个函数可以不分顺序同时使用
addr.call.gas(1000000).value(1 ether)(1, "MyName");
注意
- 合约中使用的this表示当前合约地址
- 所有的合约对象都可以被转成地址类型,查询当前合约的余额
address(this).balance
this.balance
值类型——定长字节数组
bytes1, ... ,bytes32,允许值以步长1递增。byte默认表示bytes1。
操作:
- 比较:<=,<,==,!=,>=,>(评估为bool)
- 位运算符:&,|,^(按位异或),~(逐位否定),<<(左移),>>(右移)
- 索引访问:如果x的类型为bytesI,则0 <= k <I的x [k]返回第k个字节(只读)。
成员:
.length产生字节数组的固定长度(只读)。
值类型——常量(有理数、整数、字符串常量和十六进制常量)
整数常量和有理数常量均支持科学计数法。基数可以是小数,指数必须是整数。 例如2e10,-2e10,2e-10,2.5e10
有理数常量(特指小数常量):
- 有数常量带一个.,在.的两边至少要有一个数字,有效的表示如下1.,.1,1.2
- 不允许位运算,小数不能用作指数
- 有理数本身支持任意精度,任何运算不会发生溢出或除法截断,当被转换成对应的其他类型,或者与其他类型运算时,不再保证精度。
整数常量:
- 由一系列0-9的数字组成的10进制数,不存在八进制的表示形式。
- 整数常量表达式的除法运算,不做截断处理,结果是有理数。
注意
数字常量表达式一旦其中含有非常量表达式,它就会被转为一个非常量表达式
pragma solidity ^0.4.0;
contract IntegerLiteralConvert{
function literalTest(){
uint128 a = 1;
//uint128 b = 2.5 + a + 0.5;
//2.5+a不能转换成一个非常量表达式
}
}
字符串常量:
字符串字面量是用双引号或单引号(“foo”或‘bar’)编写,长度可变。 它们不像C语言那样默认以0结尾; “foo”代表三个不是四个字节。 它们可以隐式转换为bytes1,…,bytes32。
pragma solidity ^0.4.0;
contract StringLiteralsTest{
bytes5 public name;
function setName(bytes5 _name){
name = _name;
}
}
十六进制常量
十六进制常量,以关键字hex打头,后面紧跟用单或双引号包裹的字符串。如hex"001122ff"。 在内部会被表示为二进制流。通过下面的例子来理解下是什么意思:
pragma solidity ^0.4.0;
contract HexLiteral{
function test() returns (string){
var a = hex"7a6f757965";
//var b = hex"a";
//由于一个字节是8位,所以一个hex是由两个[0-9a-z]字符组成的。所以var b = hex“A”;不是成双的,转字符串是会报错的
return a;
}
}
//十六进制的常量与字符串可以进行同样的类似操作:
pragma solidity ^0.4.0;
contract HexLiteralBytes{
function test() returns (bytes4, bytes1, bytes1, bytes1, bytes1){
bytes4 a = hex"001122FF";
return (a, a[0], a[1], a[2], a[3]);
}
}
值类型——枚举
枚举类型是在Solidity中的一种用户自定义类型。
pragma solidity ^0.4.16;
contract test {
enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
ActionChoices choice;
ActionChoices constant defaultChoice = ActionChoices.GoStraight;
function setGoStraight() public {
choice = ActionChoices.GoStraight;
// choice = ActionChoices(2);
}
function getChoice() public view returns (ActionChoices) {
return choice;
}
function getDefaultChoice() public pure returns (uint) {
return uint(defaultChoice);
}
}
注意:
- 枚举可以显示的转换与整数进行转换,但不能进行隐式转换。显示的转换会在运行时检查数值范围,如果不匹配,将会引起异常。
- 枚举类型应至少有一名成员。
值类型——函数
function关键字声明的,合约中的可执行单元
完整的函数的定义如下:
function (<parameter types>) {internal|external} [constant] [payable] [returns (<return types>)]
函数的可见性
solidity的函数具有四种可见性:public、external、internal、private 默认public
内部函数(internal)
内部函数只能在所属合约,以及继承了所属合约的子合约中调用,调用方式是直接使用函数名
f()
外部函数(external)
外部函数可以在所属合约以外的地方调用,如果在所属合约内部调用也要使用外部函数的调用方式
this.f()
私有函数(private)
私有函数只能在所属合约中调用,调用方式是直接使用函数名
f()
公有函数(public)
公有函数可以在任何地方调用,在所属合约中调用,既支持内部调用方式,也支持外部调用方式。
f()
this.f()
public 或external 函数的特殊成员——函数选择器
pragma solidity ^0.4.16;
contract Selector {
function f() public view returns (bytes4) {
return this.f.selector;
}
}
范例
pragma solidity ^0.4.5;
contract FuntionTest{
function internalFunc() internal{}
function externalFunc() external{}
function callFunc(){
//直接使用内部的方式调用
internalFunc();
//不能在内部调用一个外部函数,会报编译错误。
//Error: Undeclared identifier.
//externalFunc();
//不能通过`external`的方式调用一个`internal`
//Member "internalFunc" not found or not visible after argument-dependent lookup in contract FuntionTest
//this.internalFunc();
//使用`this`以`external`的方式调用一个外部函数
this.externalFunc();
}
}
contract FunctionTest1{
function externalCall(FuntionTest ft){
//调用另一个合约的外部函数
ft.externalFunc();
//不能调用另一个合约的内部函数
//Error: Member "internalFunc" not found or not visible after argument-dependent lookup in contract FuntionTest
//ft.internalFunc();
}
}
函数的分类
在internal和returns中间还可以加入一个配置属性,[pure|constant|view|payable]。
纯函数(pure)
既不从状态读取数据也不写入数据的函数可以被声明为纯函数 除了之前修改状态数据的情况外,我们认为一下情况属于从状态读取数据。
- 读取状态变量
- 调用this.balance或者address.balance
- 调用block、tx、msg的成员
- 调用任何非纯函数
只读函数(constant/view)
不改变状态的函数可以被声明为只读函数一下几种情况被视为修改了状态:
- 修改状态变量
- 触发事件
- 创建了其他合约的实例
- 使用了selfdestruct自我销毁
- 调用了向合约转账的函数
- 调用了非只读函数或者纯函数
- 使用了底层调用
注意:
constant是view的一个别名,会在0.5.0版本中遗弃,访问器(getter)方法默认被标记为view调用只读函数。
payable函数
是声明了该函数涉及支付操作,需要虚拟机提供事务支持,如果不声明的话,函数关于以太币交易的操作都会被拒回。
特殊的函数——回退函数
每一个合约可以有且仅可以有一个没有名字的函数。这个函数无参数,也无返回值。如果调用合约时,没有匹配上任何一个函数,就会调用默认的回退函数。
一个没有定义一个回退函数的合约。如果接收ether,会触发异常,并返还ether(solidity v0.4.0开始)。所以合约要接收ether,必须实现回退函数。下面来看个例子:
pragma solidity ^0.4.0;
contract Test {
uint x;
function() public payable {
x = 1;
}
function getX() view returns(uint){
return x;
}
function getBalance() view returns(uint){
return this.balance;
}
}
contract Caller {
function Caller()payable{}
function callTest(Test test) public {
test.call(0xabcdef01);
// test.transfer(2 ether);
}
function getBalance() view returns(uint){
return this.balance;
}
}
函数的输入参数与输出参数
Solidity函数的输入参数的数量是可选的,也可以有任意数量的返回参数。
入参(Input Parameter)与变量的定义方式一致,稍微不同的是,不会用到的参数可以省略变量名称。一种可接受两个整型参数的函数如下:
pragma solidity ^0.4.0;
contract Simple {
function taker(uint _a, uint) {
// do something with _a.
}
}
出参(Output Paramets)在returns关键字后定义,语法类似变量的定义方式。返回结果的数量需要与定义的一致。如果给定了参数名,则函数可以不适用return关键字返回,如果没有给定参数名则需要函数体重使用return关键字按照顺序返回。
pragma solidity ^0.4.0;
contract Simple {
//return sum and product
function arithmetics(uint _a, uint _b) returns (uint o_sum, uint o_product) {
o_sum = _a + _b;
o_product = _a * _b;
}
}
pragma solidity ^0.4.0;
contract Simple {
//return sum and product
function arithmetics(uint _a, uint _b) pure returns (uint , uint ) {
return(_a + _b,_a * _b);
}
}
函数重载
一个合约可以有多个名字相同、参数不同的函数,这些函数被称为重载函数。
要求:
输入参数类型不同或者输入参数个数不同(从本质上来说就是在调用的时候可以明确的区分开到底调用的是哪个函数,重载函数之前除了名字相同别无关联)