源文件包括任意数量的合约定义、include指令和pragma指令。
源文件可以(应该)用一个所谓的版本注释来注释,以防止被编译以后可能会引入不兼容的编译器版本。 我们试图将这种变化保持在绝对最低限度,尤其是引入更改语义的方式也需要语法的改变,但这当然不总是可能的。 因此至少对于包含版本的突发更改,可以通过阅读更新日志,这些版本有0.x.0或x.0.0格式。
版本语法格式:
pragma solidity ^0.4.0;
类似JS的Import语句,但是Solidity不支持“default export”。
全局:
import "filename";
从“filename”中导入所有的全局symbols到当前全局范围
import * as symbolName from "filename";
创建新的全局symbol symbolName
,其成员都是来自“filename”的全局symbols。
import {symbol1 as alias, symbol2} from "filename";
创建新的全局symbols“alias”和“symbol2”,它将分别从”filename” 引入symbol1 和 symbol2。
更方便的语法
import "filename" as symbolName;
等价于
import * as symbolName from "filename";
. 当前目录;
.. 父目录;
不用.开头的都视为绝对路径。
从相同目录下import一个文件x作为当前文件:import "./x" as x;
。
import “x” as x;
是不同的文件引用(在全局中使用”include directory”)。
solc:命令行编译器,重映射context:prefix=target
,context:和=target部分都是可以选择的。例如:
如果你从github.com/ethereum/dapp-bin/
克隆到本地/usr/local/dapp-bin
,可以在源文件中使用以下内容:
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;
运行编译器:
solc github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ source.sol
作为一个更复杂的示例,假设您依赖使用一个非常旧版本的dapp-bin的模块。旧版本的dapp-bin在/usr/local/dapp-bin_old
可以使用:
solc module1:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin/ \
module2:github.com/ethereum/dapp-bin/=/usr/local/dapp-bin_old/ \
source.sol
module2中为旧版本;
module1中为新版本。
Remix:提供从github上的自动重映射,并且自动检索网络上的文件,可以import 迭代映射:
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;
// This is a single-line comment.
/*
This is a
multi-line comment.
*/
Solidity合约类似面向对象语言中的类。每合约可以包含声明状态变量、函数、函数修饰符、事件、结构类型和枚举类型。此外,合约可以从其他合约中继承。
状态变量是永久存储在合约存储中的值。
pragma solidity ^0.4.0;
contract SimpleStorage {
uint storedData; // State variable
// ...
}
函数是合约中的代码可执行单位。
pragma solidity ^0.4.0;
contract SimpleAuction {
function bid() payable { // Function
// ...
}
}
函数调用可以在内部或外部发生,对其他合约有不同级别的可见性。
函数修饰符可用于以声明方式修改函数的语义(参见合约部分中的函数修饰符)。
pragma solidity ^0.4.11;
contract Purchase {
address public seller;
modifier onlySeller() { // **Modifier**
require(msg.sender == seller);
_;
}
function abort() onlySeller { // **Modifier usage**
// ...
}
}
事件是与EVM日志设施的方便接口。
pragma solidity ^0.4.0;
contract SimpleAuction {
event HighestBidIncreased(address bidder, uint amount); // Event
function bid() payable {
// ...
HighestBidIncreased(msg.sender, msg.value); // Triggering event
}
}
请参见合约部分中的事件,了解如何声明事件和如何在dapp中使用事件。
Structs是自定义定义的类型,可以组合多个变量(参见类型部分中的struct)。
pragma solidity ^0.4.0;
contract Ballot {
struct Voter { // Struct
uint weight;
bool voted;
address delegate;
uint vote;
}
}
枚举可以用于创建具有有限值集的自定义类型(参见类型部分中的枚举)。
pragma solidity ^0.4.0;
contract Purchase {
enum State { Created, Locked, Inactive } // Enum
}
Solidity是一种静态类型的语言,这意味着在编译时需要指定每个变量(state和local)的类型。Solidity提供了几种基本类型,可以组合成复杂类型。
值类型:值类型的变量总是按值传递的,作为函数参数或者在赋值中,总需要拷贝。
(1)布尔
bool: 为真or假
!:逻辑否
&&: 逻辑与;类似and
||:逻辑或,类似or
==:等
!=:不等
(2)整型
int/uint: 整型/无符号整型
(3)定点数(Fixed Point Numbers)
Solidity还没有完全的支持定点数。可以被声明,但不能被assigned或者from
fixed/ufixed;
ufixedMxN / fixedMxN, M表示按类型所取的位数,N表示有多少位小数。
(4)地址
address:保存一个20字节的值(一个以太地址的大小)。地址类型也有成员,并作为所有合约的基础。
从版本0.5.0开始,合约不是从地址类型派生出来的,但仍然可以显式地转换为地址。
(5)地址成员
可以利用balance查询地址的余额,并利用transfer给地址发送以太币。
address x = 0x123;
address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);
类似transfer的功能。一旦执行失败,当前的合约不会停止,但是send会返回false。
注意:使用send有一些危险:如果调用堆栈深度在1024(可能被调用者强制),则传输失败;如果接收方耗尽了gas,也会失败。因此为了保证安全的Ether传输,总是检查send的返回值,使用transfer,或者使用接收方提取资金的模式更好。
另外,合约的接口不是附在ABI 上,函数调用可以引用任意数量的参数,这些参数要填补成32字节,并被拼接。一个例外的情况是第一个参数被确切的编码成4字节,这种情况下,不用填补,直接使用函数符号。
address nameReg = 0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2;
nameReg.call("register", "MyName");
nameReg.call(bytes4(keccak256("fun(uint256)")), a);
call返回了一个布尔值,表示函数是否是正常调用结束(true)或引起了EVM异常(false)。不可能访问返回实际数据(这个我们需要提前知道编码和大小)。
可以用. gas() modifier调整所供应的gas:
namReg.call.gas(1000000)("register", "MyName");
以太币也可以:
nameReg.call.value(1 ether)("register", "MyName");
这些modifier可以组织在一起,顺序随意:
nameReg.call.gas(1000000).value(1 ether)("register", "MyName");
注意:
不能使用gas和value修改器在重载函数中。
类似的,delegatecall函数:不同之处是它不仅使用给定地址的代码,其他的方面(storage,balance,。。。)从当前的合约中获取。delegatecall的目的是使用存储在另一个合约中的库代码。用户需要保证在两个合约中的存储布局适合于delegatecall的使用。
callcode的使用不需要提供原始的msg.sender和msg.value的值。
.gas()三个函数都可以使用,.value()不支持delegatecall。
(6)固定大小的数组
bytes1, bytes2, bytes3, …, bytes32.
byte 是 bytes1的别名.
.length
生成固定长度的数组(只读)注意:也可以使用字节数组byte[ ],但是这个会浪费很多空间,最好使用bytes。
(7)动态数组
bytes:动态大小字节数据,不是一个值类型。
string:动态大小utf-8编码的字符串,不是一个值类型。
(8)地址常量?(Address Literals)
通过地址校验和测试的十六进制 literals ,例如:0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF
为地址类型。
(9)有理数和整数Literals
Solidity没有8进制数;
支持科学计数法;
Number literal expressions:数字文字表达式保留任意的精度,直到它们被转换成non-literal 类型(也就是说,用non-literal 的表达式一起使用它们)。这意味着计算不会溢出,而分隔不会在数字文字表达式中截断。(分割?截断?)
这里的意思好像是结果都是整数?
还有就是1+2和2+1的意思是一样的。
(10)String Literals
字符串常量写作:“foo”或者‘bar’,字符串不像C,不包含结束符。
(11)定长字节数组
字符串的长度类型可以是变长的。可以隐式的转换为:bytes1, bytes2, bytes3, …, bytes32.
pragma solidity ^0.4.0;
contract StringConvert{
function test() returns (bytes3){
bytes3 a = "123";
//bytes3 b = "1234";
//Error: Type literal_string "1234" is not implicitly convertible to expected type bytes3.
return a;
}
}
(12)转义字符
\xNN表示16进制。
(13)16进制字面量(Literals)
以关键字hex打头,后面为单引号或者双引号包裹的字符串。如hex”001122ff”。
pragma solidity ^0.4.0;
contract HexLiteral{
function test() returns (string){
var a = hex"001122FF";
//var b = hex"A";
//Expected primary expression
return a;
}
}
hex后面必须是双数,因为hex是由两个[0-9a-z]组成的。
16进制的字面量与字符串可以进行类似操作:
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]);
}
}
可以发现,它可以隐式的转为bytes,上述代码的执行结果如下:
Result: "0x001122ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000000000000000000000"
Transaction cost: 21857 gas.
Execution cost: 585 gas.
Decoded:
bytes4: 0x001122ff
bytes1: 0x00
bytes1: 0x11
bytes1: 0x22
bytes1: 0xff
(14)枚举
一种用户自定义类型,可以显示的进行与整数进行转换,但不能进行隐式的转换。显示转换会在运行时检查数值范围,如果不匹配,会引起异常。枚举类型应至少有一个成员。
pragma solidity ^0.4.0;
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);
}
}
(15)函数类型
可以将函数赋值给一个函数类型的变量,可以将函数作为参数进行传递,可以在函数调用中返回一个函数。
有两种类型:internal,external。
内部函数(internal)
不能再当前合约的上下文环境以外的地方执行,内部韩束只能在当前合约内部被使用。如在当前的代码块内,包括内部库函数,和继承的函数中。
外部函数(external)
外部函数由一个地址和一个函数签名组成,它们可以通过并从外部函数调用返回。
函数的定义
function () {internal(默认)|external} [constant] [payable] [returns ()]
不写类型的话,默认为internal。
pragma solidity ^0.4.0;
contract Test{
//默认是internal类型的
function noParameter() returns (uint){}
//无返回结果
function noReturn1(uint x) {}
//如果无返回结果,必须省略`returns`关键字
//function noReturn2(uint x) returns {}
}
如果一个函数变量没有初始化,直接调用它将会产生异常。如果delete了一个函数后调用,也会发生同样的异常。
如果外部函数类型在Solidity的上下文环境以外的地方使用,他们会被视为function类型。编码为20字节的函数所在地址,紧跟4字节的函数方法签名的共占24字节的bytes24类型。
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();
}
}
pragma solidity ^0.4.5;
library ArrayUtils {
// internal functions can be used in internal library functions because
// they will be part of the same code context
function map(uint[] memory self, function (uint) returns (uint) f)
internal
returns (uint[] memory r)
{
r = new uint[](self.length);
for (uint i = 0; i < self.length; i++) {
r[i] = f(self[i]);
}
}
function reduce(
uint[] memory self,
function (uint, uint) returns (uint) f
)
internal
returns (uint r)
{
r = self[0];
for (uint i = 1; i < self.length; i++) {
r = f(r, self[i]);
}
}
function range(uint length) internal returns (uint[] memory r) {
r = new uint[](length);
for (uint i = 0; i < r.length; i++) {
r[i] = i;
}
}
}
contract Pyramid {
using ArrayUtils for *;
function pyramid(uint l) returns (uint) {
return ArrayUtils.range(l).map(square).reduce(sum);
}
function square(uint x) internal returns (uint) {
return x * x;
}
function sum(uint x, uint y) internal returns (uint) {
return x + y;
}
}
pragma solidity ^0.4.11;
contract Oracle {
struct Request {
bytes data;
function(bytes memory) external callback;
}
Request[] requests;
event NewRequest(uint);
function query(bytes data, function(bytes memory) external callback) {
requests.push(Request(data, callback));
NewRequest(requests.length - 1);
}
function reply(uint requestID, bytes response) {
// Here goes the check that the reply comes from a trusted source
requests[requestID].callback(response);
}
}
contract OracleUser {
Oracle constant oracle = Oracle(0x1234567); // known contract
function buySomething() {
oracle.query("USD", this.oracleResponse);
}
function oracleResponse(bytes response) {
require(msg.sender == address(oracle));
// Use the data
}
}
其实并看不懂。。。
复杂类型,占用的空间更大,超过256字节,因为拷贝他们占用更多的空间。因此,我们需要考虑将他们存储在什么位置:内存(memory,数据不是永久存在的)或存储(storage,值类型中的状态变量)。
(1)数据位置
这些复杂类型:数组,结构体,在Solidity有一个额外的属性——数据的存储位置。可选为memory和storage。
memory存储位置和普通程序的内存一致。即分配即使用,越过作用域将不可被访问,等待被回收。区块链底层实现了图灵完备,所以有非常多的状态需要永久记录下来。比如,参与众筹的所有参与者。所以,需要使用storage类型,一旦使用storage这个类型,数据将永远存在。
默认函数参数,返回参数为memory;默认局部变量为storage;默认状态变量(合约声明的public变量)为storage。
calldata:存储的为函数参数,为只读类型,不会永久的存储一个数据位置。外部函数的参数(不包括返回的参数)强制指定为calldata。效果为memory类似。
不同数据位置变量赋值产生的结果不同。
将一个storage的状态变量,赋值给一个storage的局部变量,是通过引用传递。所以对于局部变量的修改,同时修改关联的状态变量。但另一方面,将一个memory的引用类型赋值给另一个memory的引用,不会创建另一个拷贝。
pragma solidity ^0.4.0;
contract DataLocation{
uint valueType;
mapping(uint => uint) public refrenceType;
function changeMemory(){
var tmp = valueType;
tmp = 100;
}
function changeStorage(){
var tmp = refrenceType;
tmp[1] = 100;
}
function getAll() returns (uint, uint){
return (valueType, refrenceType[1]);
}
}
pragma solidity ^0.4.0;
contract C {
uint[] x; // the data location of x is storage
// the data location of memoryArray is memory
function f(uint[] memoryArray) {
x = memoryArray; // works, copies the whole array to storage
var y = x; // works, assigns a pointer, data location of y is storage
y[7]; // fine, returns the 8th element
y.length = 2; // fine, modifies x through y
delete x; // fine, clears the array, also modifies y
// The following does not work; it would need to create a new temporary /
// unnamed array in storage, but storage is "statically" allocated:
// y = memoryArray;
// This does not work either, since it would "reset" the pointer, but there
// is no sensible location it could point to.
// delete y;
g(x); // calls g, handing over a reference to x
h(x); // calls h and creates an independent, temporary copy in memory
}
function g(uint[] storage storageArray) internal {}
function h(uint[] memoryArray) {}
}
(2)数组
数组可以声明指定长度,或者变长。对storage的数组来说,元素类型可以是任意的,类型可以是数组,映射类型,数据结构等。但对于memory的数组来说,如果函数是对外可见的,那么函数参数不能是映射类型的数组,只能是支持ABI的类型。
一个类型为T,长度为k的数组,可以声明为T[k],而一个变长的数组则声明为T[]。
你还可以声明一个多维数据,如5个类型为uint的变长数组,可以声明为uint[][5] x。需要留心的是,相比非区块链语言,多维数组的长度声明是反的。
要访问第三个动态数据的,第二个元素,使用x[2][1]。数组的序号是从0开始的,序号顺序与定义相反。
bytes和string是一种特殊的数组。bytes类似byte[],但在外部函数作为参数调用中,会进行压缩打包,更省空间,所以应该尽量使用bytes4。string类似bytes,但不提供长度和按序号的访问方式。
由于bytes与string,可以自由转换,你可以将字符串s通过bytes(s)转为一个bytes。但需要注意的是通过这种方式访问到的是UTF-8编码的码流,并不是独立的一个个字符。比如中文编码是多字节,变长的,所以你访问到的很有可能只是其中的一个代码点。
类型为数组的状态变量,可以标记为public类型,从而让Solidity创建一个访问器,如果要访问数组的某个元素,指定数字下标就好了。
可使用new关键字创建一个memory的数组。与stroage数组不同的是,你不能通过.length的长度来修改数组大小属性。
pragma solidity ^0.4.0;
contract C {
function f() {
//创建一个memory的数组
uint[] memory a = new uint[](7);
//不能修改长度
//Error: Expression has to be an lvalue.
//a.length = 100;
}
//storage
uint[] b;
function g(){
b = new uint[](7);
//可以修改storage的数组
b.length = 10;
b[9] = 100;
}
}
数组字面量,作为表达式编写的数组,不立即分配给其一个变量。
pragma solidity ^0.4.0;
contract C {
function f() {
g([uint(1), 2, 3]);
}
function g(uint[3] _data) {
// ...
}
}
定长数组不能与变长数组相互赋值。
数组有一个.length属性,表示当前的数组长度。storage的变长数组,可以通过给.length赋值调整数组长度。memory的变长数组不支持。
不能通过访问超出当前数组的长度的方式,来自动实现上面说的这种情况。memory数组虽然可以通过参数,灵活指定大小,但一旦创建,大小不可调整,对于变长数组,可以通过参数在编译期指定数组大小。
storage的变长数组和bytes都有一个push(),用于附加新元素到数据末端,返回值为新的长度。
pragma solidity ^0.4.0;
contract C {
uint[] u;
bytes b;
function testArryPush() returns (uint){
uint[3] memory a = [uint(1), 2, 3];
u = a;
return u.push(4);
}
function testBytesPush() returns (uint){
b = new bytes(3);
return b.push(4);
}
}
当前在外部函数中,不能使用多维数组。
另外,基于EVM的限制,不能通过外部函数返回动态的内容。
pragma solidity ^0.4.0;
contract C {
function f() returns (uint[]) {
}
}
在上面的例子中,通过web.js调用能返回数据,但在Solidity中不能返回数据。一种临时的解决办法,是使用一个非常大的静态数组。
pragma solidity ^0.4.0;
contract ArrayContract {
uint[2**20] m_aLotOfIntegers;
// Note that the following is not a pair of dynamic arrays but a
// dynamic array of pairs (i.e. of fixed size arrays of length two).
bool[2][] m_pairsOfFlags;
// newPairs is stored in memory - the default for function arguments
function setAllFlagPairs(bool[2][] newPairs) {
// assignment to a storage array replaces the complete array
m_pairsOfFlags = newPairs;
}
function setFlagPair(uint index, bool flagA, bool flagB) {
// access to a non-existing index will throw an exception
m_pairsOfFlags[index][0] = flagA;
m_pairsOfFlags[index][1] = flagB;
}
function changeFlagArraySize(uint newSize) {
// if the new size is smaller, removed array elements will be cleared
m_pairsOfFlags.length = newSize;
}
function clear() {
// these clear the arrays completely
delete m_pairsOfFlags;
delete m_aLotOfIntegers;
// identical effect here
m_pairsOfFlags.length = 0;
}
bytes m_byteData;
function byteArrays(bytes data) {
// byte arrays ("bytes") are different as they are stored without padding,
// but can be treated identical to "uint8[]"
m_byteData = data;
m_byteData.length += 7;
m_byteData[3] = 8;
delete m_byteData[2];
}
function addFlag(bool[2] flag) returns (uint) {
return m_pairsOfFlags.push(flag);
}
function createMemoryArray(uint size) returns (bytes) {
// Dynamic memory arrays are created using `new`:
uint[2][] memory arrayOfPairs = new uint[2][](size);
// Create a dynamic byte array:
bytes memory b = new bytes(200);
for (uint i = 0; i < b.length; i++)
b[i] = byte(i);
return b;
}
}
(3)结构体
pragma solidity ^0.4.0;
contract CrowdFunding{
struct Funder{
address addr;
uint amount;
}
struct Campaign{
address beneficiary;
uint goal;
uint amount;
uint funderNum;
mapping(uint => Funder) funders;
}
uint compaingnID;
mapping (uint => Campaign) campaigns;
function candidate(address beneficiary, uint goal) returns (uint compaingnID){
// initialize
campaigns[compaingnID++] = Campaign(beneficiary, goal, 0, 0);
}
function vote(uint compaingnID) payable {
Campaign c = campaigns[compaingnID];
//another way to initialize
c.funders[c.funderNum++] = Funder({addr: msg.sender, amount: msg.value});
c.amount += msg.value;
}
function check(uint comapingnId) returns (bool){
Campaign c = campaigns[comapingnId];
if(c.amount < c.goal){
return false;
}
uint amount = c.amount;
// incase send much more
c.amount = 0;
if(!c.beneficiary.send(amount)){
throw;
}
return true;
}
}
上面的代码向我们展示的一个简化版的众筹项目,其实包含了一些struct的使用。struct可以用于映射和数组中作为元素。其本身也可以包含映射和数组等类型。
我们不能声明一个struct同时将这个struct作为这个struct的一个成员。这个限制是基于结构体的大小必须是有限的。
虽然数据结构能作为一个mapping的值,但数据类型不能包含它自身类型的成员,因为数据结构的大小必须是有限的。
需要注意的是在函数中,将一个struct赋值给一个局部变量(默认是storage类型),实际是拷贝的引用,所以修改局部变量值时,会影响到原变量。
当然,你也可以直接通过访问成员修改值,而不用一定赋值给一个局部变量,如campaigns[comapingnId].amount = 0
映射或字典类型,一种键值对的映射关系存储结构。键的类型允许除映射外的所有类型,如数组,合约,枚举,结构体。值的类型无限制。
定义方式:mapping(_KeyType => _KeyValue)
,这里_KeyType
可以使任意类型
delete运算符,用于将某个变量重置为初始值。对于整数,运算符的效果等同于a = 0。而对于定长数组,则是把数组中的每个元素置为初始值,变长数组则是将长度置为0。对于结构体,也是类似,是将所有的成员均重置为初始值。
delete对于映射类型几乎无影响,因为键可能是任意的,且往往不可知。所以如果你删除一个结构体,它会递归删除所有非mapping的成员。当然,你是可以单独删除映射里的某个键,以及这个键映射的某个值。
pragma solidity ^0.4.0;
contract DeleteExample{
uint a;
function f() returns (uint){
int8 y = -3;
uint x = uint(y);
return x;
}
}
如果运算符支持两边不同的类型,编译器会尝试隐式转换类型,同理,赋值时也是类似。通常,隐式转换需要能保证不会丢失数据,且语义可通。如uint8可以转化为uint16,uint256。但int8不能转为uint256,因为uint256不能表示-1。
此外,任何无符号整数,可以转换为相同或更大大小的字节值。比如,任何可以转换为uint160的,也可以转换为address。
为了方便,并不总是需要明确指定一个变量的类型,编译器会通过第一个向这个对象赋予的值的类型来进行推断。
uint24 x = 0x123;
var y = x;
函数的参数,包括返回参数,不可以使用var这种不指定类型的方式。
需要特别注意的是,由于类型推断是根据第一个变量进行的赋值。所以代码for (var i = 0; i < 2000; i++) {}将是一个无限循环,因为一个uint8的i的将小于2000。
pragma solidity ^0.4.4;
contract Test{
function a() returns (uint){
uint count = 0;
for (var i = 0; i < 2000; i++) {
count++;
if(count >= 2100){
break;
}
}
return count;
}
}
参考资料:
http://www.tryblockchain.org/Solidity-TypeDeduction-%E7%B1%BB%E5%9E%8B%E6%8E%A8%E6%96%AD.html
http://solidity.readthedocs.io/en/develop/solidity-in-depth.html