复杂类型,如数组(arrays)
和数据结构(struct)
在Solidity中有一个额外的属性,数据的存储位置。可选为memory
和storage
。
memory
存储位置同我们普通程序的内存一致。即分配,即使用,越过作用域即不可被访问,等待被回收。而在区块链上,由于底层实现了图灵完备,故而会有非常多的状态需要永久记录下来。比如,参与众筹的所有参与者。那么我们就要使用storage
这种类型了,一旦使用这个类型,数据将永远存在。
基于程序的上下文,大多数时候这样的选择是默认的,我们可以通过指定关键字storage
和memory
修改它。
默认的函数参数,包括返回的参数,他们是memory
。默认的局部变量是storage
的1。而默认的状态变量(合约声明的公有变量)是storage
。
另外还有第三个存储位置calldata
。它存储的是函数参数,是只读的,不会永久存储的一个数据位置。外部函数
的参数(不包括返回参数)被强制指定为calldata
。效果与memory
差不多。
数据位置指定非常重要,因为不同数据位置变量赋值产生的结果也不同。在memory
和storage
之间,以及它们和状态变量
(即便从另一个状态变量)中相互赋值,总是会创建一个完全不相关的拷贝。
将一个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) {}
}
外部函数(External function)
的参数(不包括返回参数)强制为:calldata
状态变量(State variables)
强制为: storage
memory
storage
更多请查看关于数据位置的进一步挖掘: http://me.tryblockchain.org/solidity-data-location.html
本地变量但是为什么是stroage
的,没有太想明白。 http://ethereum.stackexchange.com/questions/9781/solidity-default-data-location-for-local-vars ↩
复杂类型。不同于之前值类型,复杂类型占的空间更大,超过256字节,因为拷贝它们占用更多的空间。由此我们需要考虑将它们存储在什么位置内存(memory,数据不是永久存在的)
或存储(storage,值类型中的状态变量)
后面我们会讲到常见的引用类型。
函数类型1即是函数这种特殊的类型。
函数类型有两类;可分为internal
和external
函数。
因为不能在当前合约的上下文环境以外的地方执行,内部函数只能在当前合约内被使用。如在当前的代码块内,包括内部库函数,和继承的函数中。
外部函数由地址和函数方法签名两部分组成。可作为外部函数调用
的参数,或者由外部函数调用
返回。
完整的函数的定义如下:
function () {internal(默认)|external} [constant] [payable] [returns (<return types>)]
若不写类型,默认的函数类型是internal
的。如果函数没有返回结果,则必须省略returns
关键字。下面我们通过一个例子来了解一下。
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字节的函数方法签名2的共占24字节的bytes24
类型。
合约中的public
的函数,可以使用internal
和external
两种方式来调用。下面来看看,两种方式的不同之处。
internal
与external
:调用一个函数f()
时,我们可以直接调用f()
,或者使用this.f()
。但两者有一个区别。前者是通过internal
的方式在调用,而后者是通过external
的方式在调用。请注意,这里关于this
的使用与大多数语言相背。下面通过一个例子来了解他们的不同:
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.0;
library ArrayUtils{
function range(uint length) internal returns (uint[] memory r){
r = new uint[](length);
for(uint i = 0; i < length; i++){
r[i] = i;
}
}
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 x, uint y) returns(uint) f
)
internal
returns (uint r)
{
r = self[0];
for(uint i = 1; i < self.length; i++){
r = f(r, self[i]);
}
}
}
contract Pyramid{
using ArrayUtils for *;
function pryamid(uint length) returns (uint){
return ArrayUtils.range(length).map(square).reduce(sum);
}
function square(uint x) returns (uint){
return x * x;
}
function sum(uint x, uint y) returns (uint){
return x + y;
}
}
Question?
library
是什么呢。library
引入时为什么使用using
,这和文件引入的import
有何区别。library
内的函数全是internal
的。library
内的函数,他的参数函数为什么是internal
的,不应该是external
的?uint[]
是什么类型,不能写做[]uint
memory
又是什么呢,为什么map
函数明明是两个参数,但只需要传一个呢。http://solidity.readthedocs.io/en/develop/types.html#function-types ↩
函数签名的编码方式可查看函数选择器相关章节,【文档翻译系列】ABI详解 ↩
枚举类型是在Solidity中的一种用户自定义类型。他可以显示的转换与整数进行转换,但不能进行隐式转换。显示的转换会在运行时检查数值范围,如果不匹配,将会引起异常。枚举类型应至少有一名成员。我们来看看下面的例子吧。
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);
}
}
十六进制字面量,以关键字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;
}
}
由于一个字节是8位,所以一个hex
是由两个[0-9a-z]
字符组成的。所以var b = hex"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]);
}
}
可以发现,它可以隐式的转为bytes
,上述代码的执行结果如下:
Result: "0x001122ff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000011000000000000000000000000000000000000000000000000000000000000002200000000000000000000000000000000000000000000000000000000000000ff00000000000000000000000000000000000000000000000000000000000000"
Transaction cost: 21857 gas.
Execution cost: 585 gas.
Decoded:
bytes4: 0x001122ff
bytes1: 0x00
bytes1: 0x11
bytes1: 0x22
bytes1: 0xff
字符串字面量是指由单引号,或双引号引起来的字符串。字符串并不像C语言,包含结束符,foo
这个字符串大小仅为三个字节。
正如整数一样,字符串的长度类型可以是变长的。特殊之处在于,可以隐式的转换为byte1
,...byte32
。下面来看看这个特性:
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;
}
}
上述的字符串字面量,会隐式转换为bytes3
。但这样不是理解成bytes3
的字面量方式一个意思。
字符串字面量支持转义字符,比如\n
,\xNN
,\uNNNN
。其中\xNN
表式16进制值,最终录入合适的字节。而\uNNNN
表示Unicode
码点值,最终会转换为UTF8
的序列。
//文档上称,暂不支持
如果字面量计算的结果不是一个整数,那么将会转换为一个对应的ufixed
,或fixed
类型。Solidity
会选择合适的大小,以能尽量包含小数部分。
例,在var x = 1 / 4
中,x
的实际类型是ufixed0x8
。而在var x = 1/ 3
中,类型会是ufixedox256
,因为这个结果表示是无限的,所以他只能是无限接近。
适用于整型的操作符,同时适用于数字的字面量运算表达式,当操作结果是整数时。如果有任何一方是有理数,将不允许使用位操作符。如果指数是小数,还将不能进行取幂运算。
Solidity对每一个有理数都有一个数值字面量类型。整数字面量和有理数字面量从属于数字面量。所有的数字字面表式的结果都属于数字字面类型。所以1 + 2
和2 + 1
都属于同样的有理数的数字字面类型3
大多数含小数的十进制,均不可被二进制准确表达,比如5.3743
的类型可能是ufixed8*248
。如果你想使用这样的值,需要明确指定精度x + ufixed(5.3743)
,否则会报类型转换错误。
整数上的字面量除法,在早期的版本中是被截断的,但现在可以被转为有理数了,如5 /2
的值为 2.5
。
数字的字面量表达式,一旦其中含有非字面量表达式,它就会被转为一个非字面量类型。下面代码中表达式的结果将会被认为是一个有理数:
pragma solidity ^0.4.0;
contract IntegerLiteralConvert{
function literalTest(){
uint128 a = 1;
//uint128 b = 2.5 + a + 0.5;
//Error: Operator + not compatible with types rational_const 5/2 and uint128
}
}
虽然我们知道上述表达式运算的结果将是一个整型,但最终被编译器认为是小数型,所以上述代码编译不能通过。
bytes1
, ... ,bytes32
,允许值以步长1
递增。byte
默认表示byte1
。
比较:<=
,<
,==
,!=
,>=
,>
,返回值为bool
类型。
位运算符:&
,|
,^
(异或),~
非
支持序号的访问,与大多数语言一样,取值范围[0, n),其中n
表示长度。
.length
表示这个字节数组的长度(只读)。
bytes
: 动态长度的字节数组,参见数组(Arrays)。非值类型1。
string
: 动态长度的UTF-8编码的字符类型,参见数组(Arrays)。非值类型1。
一个好的使用原则是:
bytes
用来存储任意长度的字节数据,string
用来存储任意长度的UTF-8
编码的字符串数据。byte1
到byte32
中的一个,因为这样更省空间。值类型与引用类型与引用类型的区别参见这里 ↩
地址: 以太坊地址的长度,大小20个字节,160位,所以可以用一个uint160
编码。地址是所有合约的基础,所有的合约都会继承地址对象,也可以随时将一个地址串,得到对应的代码进行调用。当然地址代表一个普通帐户时,就没有这么多丰富的功能啦。
<=
,<
,==
,!=
,>=
和>
属性:balance
函数:send()
,call()
,delegatecall()
,callcode()
。
十六进制的字符串,凡是能通过地址合法性检查(address checksum test)2,就会被认为是地址,如0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF
。需要注意的是39到41位长的没有通过地址合法性检查的,会提示一个警告,但会被视为普通的有理数字面量。
balance
通过它能得到一个地址的余额。
pragma solidity ^0.4.0;
contract addressTest{
function getBalance(address addr) returns (uint){
return addr.balance;
}
}
我们可以把上述代码放入remix中,看看效果,参见下面的操作演示:
演示中的一个核心要点是,编译后,我们能得到当前合约的地址,并将该地址复制到输入框中,记得录入地址项时要加英文的双引号,否则会报Error encoding arguments: SyntaxError: JSON Parse error: Expected ']'
。
this
如果只是想得到当前合约的余额,其实可以这样写:
pragma solidity ^0.4.0;
contract addressTest{
function getBalance() returns (uint){
return this.balance;
}
}
原因是对于合约来说,地址代表的就是合约本身,合约对象默认继承自地址对象,所以内部有地址的属性。
send()
用来向某个地址发送货币(货币单位是wei
)。
pragma solidity ^0.4.0;
//请注意这个仅是Demo,请不要用到正式环境
contract PayTest {
//得到当前合约的余额
function getBalance() returns (uint) {
return this.balance;//0
}
//向当前合约存款
function deposit() payable returns(address addr, uint amount, bool success){
//msg.sender 全局变量,调用合约的发起方
//msg.value 全局变量,调用合约的发起方转发的货币量,以wei为单位。
//send() 执行的结果
return (msg.sender, msg.value, this.send(msg.value));
}
}
这个合约实现的是充值。this.send(msg.value)
意指向合约自身发送msg.value
量的以太币。msg.value
是合约调用方附带的以太币。
下面是操作演示:
关于发送者的帐号,发送的以太币数量设置,需切换到Remix的小飞机图标的配置页。要在调用deposit
前在Value
输入项填入要发的以太币数量。在getBalance
时要记得将value
项内填的值去掉,因为getBalance
方法,并不是payable
的,不支持货币3。
send()
方法执行时有一些风险
gas
不够,执行会失败。如果执行失败,将会回撤所有交易,所以务必留意返回结果。
call()
,callcode()
和delegatecall()
为了同一些不支持ABI协议的进行直接交互(一般的web3.js
,soldity
都是支持的)。可以使用call()
函数,用来向另一个合约发送原始数据。参数支持任何类型任意数量。每个参数会按规则(规则是按ABI4)打包成32字节并一一拼接到一起。
call()
方法支持ABI协议4定义的函数选择器。如果第一个参数恰好4个字节,在这种情况下,会被认为根据ABI协议定义的函数器指定的函数签名4。所以如果你只是想发送消息体,需要避免第一个参数是4个字节。
call
方法返回一个bool
值,以表明执行成功还是失败。正常结束返回true
,异常终止返回false
。我们无法解析返回结果,因为这样我们得事前知道返回的数据的编码和数据大小(这里的潜在假设是不知道对方使用的协议格式,所以也不会知道返回的结果如何解析,有点祼协议测试的感觉)。
同样我们也可以使用delegatecall()
,它与call
方法的区别在于,仅仅是代码会执行,而其它方面,如(存储,余额等)都是用的当前的合约的数据。delegatecall()
方法的目的是用来执行另一个合约中的工具库。所以开发者需要保证两个合约中的存储变量能兼容,来保证delegatecall()
能顺利执行。
在homestead阶段之前,仅有一个受限的多样的callcode()
方法可用,但并未提供对msg.sender
,msg.value
的访问权限。
上面的这三个方法call()
,delegatecall()
,callcode()
都是底层的消息传递调用,最好仅在万不得已才进行使用,因为他们破坏了Solidity的类型安全。
关于call()
函数究竟发的什么消息体,函数选择器究竟怎么用,参见这个文章的挖掘。
上述的函数都是底层的函数,使用时要异常小心。当调用一个未知的,可能是恶意的合约时,当你把控制权交给它,它可能回调回你的合约,所以要准备好在调用返回时,应对你的状态变量可能被恶意篡改的情况。
如果你想了解更多关于地址的由来,UTXO等,可以参考: http://me.tryblockchain.org/Solidity%E7%9A%84%E5%9C%B0%E5%9D%80%E7%B1%BB%E5%9E%8B.html ↩
为防止录入地址有误,一种格式化地址后来确认地址有效性的方案,https://github.com/ethereum/EIPs/issues/55 ↩
原因详见实现以太币支付的文章,http://me.tryblockchain.org/%E6%94%AF%E4%BB%98%E7%9B%B8%E5%85%B3.html ↩
关于ABI协议的详细说明:http://me.tryblockchain.org/Solidity-abi-abstraction.html ↩
int/uint
:变长的有符号或无符号整型。变量支持的步长以8
递增,支持从uint8
到uint256
,以及int8
到int256
。需要注意的是,uint
和int
默认代表的是uint256
和int256
。
支持的运算符:
比较:<=
,<
,==
,!=
,>=
,>
,返回值为bool
类型。
位运算符:&
,|
,(^
异或),(~
非)。
数学运算:+
,-
,一元运算+
,*
,/
,(%
求余),(**
平方)。
整数除法总是截断的,但如果运算符是字面量,则不会截断(后面会进一步提到)。另外除0
会抛异常 ,我们来看看下面的这个例子:
pragma solidity ^0.4.0;
// simple store example
contract simpleStorage{
uint valueStore; //
function add(uint x, uint y) returns (uint z){
z = x + y;
}
function divide() returns (uint z){
uint x = 1;
uint y = 2;
z = x / y;
}
}
整数字面量,由包含0-9的数字序列组成,默认被解释成十进制。在Solidity
中不支持八进制,前导0
会被默认忽略,如0100
,会被认为是100
。
小数由.
组成,在他的左边或右边至少要包含一个数字。如1.
,.1
,1.3
均是有效的小数。
字面量本身支持任意精度,也就是可以不会运算溢出,或除法截断。但当它被转换成对应的非字面量类型,如整数或小数。或者将他们与非字面量进行运算,则不能保证精度了。
pragma solidity ^0.4.0;
contract IntegerLiteral{
function integerTest() returns (uint, uint){
//超出运算字长了
var i = (2**800 + 1) - 2**800;
var j = 1/3*3;
//小数运算
var k = 0.5*8;
return (i, j);
}
}
总之来说就是,字面量怎么都计算都行,但一旦转为对应的变量后,再计算就不保证精度啦。
bool
: 可能的取值为常量值true
和false
.
支持的运算符:
!逻辑非
&& 逻辑与
|| 逻辑或
== 等于
!= 不等于
备注:运算符&&
和||
是短路运算符,如f(x)||g(y)
,当f(x)
为真时,则不会继续执行g(y)
。
由于Solidity是一个静态类型的语言,所以编译时需明确指定变量的类型(包括本地变量
或状态变量
),Solidity
编程语言提供了一些基本类型(elementary types)
可以用来组合成复杂类型。
类型可以与不同运算符组合,支持表达式运算,你可以通过表达式的执行顺序(Order of Evaluation of Expressions)来了解执行顺序。
值类型
包含
布尔(Booleans)
整型(Integer)
地址(Address)
定长字节数组(fixed byte arrays)
有理数和整型(Rational and Integer Literals
,String literals)
枚举类型(Enums)
函数(Function Types)
为什么会叫值类型
,是因为上述这些类型在传值时,总是值传递1。比如在函数传参数时,或进行变量赋值时。
复杂类型,占用空间较大的。在拷贝时占用空间较大。所以考虑通过引用传递。常见的引用类型有:
关于参数传递的相关方式的进一步了解: http://baike.baidu.com/item/参数传递 ↩
每个合约中可包含状态变量(State Variables)
,函数(Functions)
,函数修饰符(Function Modifiers)
,事件(Events)
,结构类型(Structs Types)
和枚举类型(Enum Types)
。
变量值会永久存储在合约的存储空间
pragma solidity ^0.4.0;
// simple store example
contract simpleStorage{
uint valueStore; //state variable
}
详情见类型(Types)
章节,关于所有支持的类型和变量相关的可见性(Visibility and Accessors)。
智能合约中的一个可执行单元。
示例
pragma solidity ^0.4.0;
contract simpleMath{
//Simple add function,try a divide action?
function add(uint x, uint y) returns (uint z){
z = x + y;
}
}
上述示例展示了一个简单的加法函数。
函数调用可以设置为内部(Internal)的和外部(External)的。同时对于其它合同的不同级别的可见性和访问控制(Visibility and Accessors)。具体的情况详见后面类型中关于函数的章节。
函数修饰符用于增强语义。详情见函数修饰符相关章节。
事件是以太坊虚拟机(EVM)日志基础设施提供的一个便利接口。用于获取当前发生的事件。
示例
pragma solidity ^0.4.0;
contract SimpleAuction {
event aNewHigherBid(address bidder, uint amount);
function bid(uint bidValue) external {
aNewHigherBid(msg.sender, msg.value);
}
}
关于事件如何声明和使用,详见后面事件相关章节。
自定义的将几个变量组合在一起形成的类型。详见关于结构体相关章节。
示例
pragma solidity ^0.4.0;
contract Company {
//user defined `Employee` struct type
//group with serveral variables
struct employee{
string name;
uint age;
uint salary;
}
//User defined `manager` struct type
//group with serveral variables
struct manager{
employee employ;
string title;
}
}
特殊的自定义类型,类型的所有值可枚举的情况。详情见后续相关章节。
示例
pragma solidity ^0.4.0;
contract Home {
enum Switch{On,Off}
}
pragma solidity ^0.4.0;
说明:
1 版本要高于0.4才可以编译
2 号表示高于0.5的版本则不可编译,第三位的版本号但可以变,留出来用做bug可以修复(如0.4.1的编译器有bug,可在0.4.2修复,现有合约不用改代码)。
import “filename”;
import * as symbolName from “filename”
分别定义引入
import {symbol1 as alias, symbol2} from “filename”
非es6兼容的简写语法
import “filename” as symbolName
等同于上述
import * as symbolName from “filename”
关于路径
引入文件路径时要注意,非.
打头的路径会被认为是绝对路径,所以要引用同目录下的文件使用
import “./x” as x
也不要使用下述方式,这样会是在一个全局的目录下
import “x” as x;
为什么会有这个区别,是因为这取决于编译器,如果解析路径,通常来说目录层级结构并不与我们本地的文件一一对应,它非常有可能是通过ipfs,http,或git建立的一个网络上的虚拟目录。
各编译器提供了文件前缀映射机制。
1. 可以将一个域名下的文件映射到本地,从而从本地的某个文件中读取
2. 提供对同一实现的不同版本的支持(可能某版本的实现前后不兼容,需要区分)
3. 如果前缀相同,取最长,
4. 有一个”fallback-remapping”机制,空串会映射到“/usr/local/include/solidify”
命令行编译器,通过下述命令命名空间映射提供支持
context:prefix=target
上述的context:
和=target
是可选的。所有context
目录下的以prefix
开头的会被替换为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,旧版本在/url/local/dapp-bin_old,那么,你可以使用下述命令编译
solc module1:github.com/ethereum/dapp-bin=/usr/local/dapp-bin \
modeule2:github.com/ethereum/dapp-bin=/usr/local/dapp-bin_old \
source.sol
需要注意的是solc仅仅允许包含实际存在的文件。它必须存在于你重映射后目录里,或其子目录里。如果你想包含直接的绝对路径包含,那么可以将命名空间重映射为=\
备注:如果有多个重映射指向了同一个文件,那么取最长的那个文件。
browser-solidity编译器默认会自动映射到github上,然后会自动从网络上检索文件。例如:你可以通过下述方式引入一个迭代包:
import “github.com/ethereum/dapp-bin/library/iterable_mapping.sol” as it_mapping
备注:未来可能会支持其它的源码方式
两种方式,单行(//
),多行使用(/*…*/
)
示例
// this is a single-line comment
/*
this is a
mulit-line comment
*/
写文档用。三个斜杠///
或/** … */
,可使用Doxygen
语法,以支持生成对文档的说明,参数验证的注解,或者是在用户调用这个函数时,弹出来的确认内容。
示例
pragma solidity ^0.4.0;
/** @title Shape calculator.*/
contract shapeCalculator{
/**
*@dev calculate a rectangle's suface and perimeter
*@param w width of the rectangles
*@param h height of the rectangles
*@return s surface of the rectangles
*@return p perimeter of the rectangles
*/
function rectangles(uint w, uint h) returns (uint s, uint p){
s = w * h;
p = 2 * ( w + h) ;
}
}