在本文中,我们将为您详细介绍【Solidity】3.类型-深入理解Solidity的相关知识,并且为您解答关于solidityenum的疑问,此外,我们还会提供一些关于根据例子学习Solidity、深
在本文中,我们将为您详细介绍【Solidity】3.类型 - 深入理解Solidity的相关知识,并且为您解答关于solidity enum的疑问,此外,我们还会提供一些关于
- 【Solidity】3.类型 - 深入理解Solidity(solidity enum)
根据例子学习Solidity 深入理解Solidity之二---Solidity源代码文件结构 - Solidity 中的基本类型转换
- Solidity 官方文档中文版 3_安装Solidity
【Solidity】3.类型 - 深入理解Solidity(solidity enum)
类型
solidity是一种静态类型的语言,这意味着每个变量(州和地方)的类型需要被指定的(或至少已知的 - 见下文型扣)在编译时。 solidity提供了几种可以组合形成复杂类型的基本类型。
另外,类型可以在含有运算符的表达式与彼此交互。 对于操作的快速参考,请参阅运算符的优先顺序。
值类型
以下类型也称为值类型,因为这些类型的变量将始终按值传递,即当它们用作函数参数或分配时,它们始终被复制。
布尔
bool
:可能的值是常量true
和false
。
操作:
-
!
(逻辑否定) -
&&
(逻辑连接,“和”) -
||
(逻辑分离,“或”) -
==
(相等) -
!=
(不等式)
运算符||
和&&
应用常见的短路规则。 这意味着在表达式f(x) || g(y)
,如果f(x)
评估为真,即使可能有副作用,也不会评估g(y)
。
整型
int/uint
:各种大小的有符号和无符号整数。 关键字uint8
到uint256
的步幅是8(无符号的8到256位)和int8
到int256
。 uint
和int
分别是uint256
和int256
的别名。
操作:
- 比较:
<=
,<
,==
,!=
,>=
,>
(评估为bool) - 位操作符:
&
,|
,^
(按位异或),〜
(按位取反) - 算术运算符:
+
,-
,一元 -
,一元 +
,*
,/
,%
(其余),**
(幂),<<
(左移),>>
(右移)
除法总是截断(它只是编译为EVM的DIV操作码),但如果两个运算符都是文字(或文字表达式),则它不会截断。
除零和模量具有零引发运行时异常。
移位操作的结果是左操作数的类型。 表达式x << y
等价于x * 2 ** y
,x >> y
等价于x / 2 ** y
。 这意味着移位负数符号延伸。 移位操作使用负数会引发运行时异常。
警告:
由符号整数类型的负值的右移位所产生的结果是从那些其他的编程语言产生的不同。 在“solidity”中,将右图转换为除法,所以偏移的负值将向零舍入(截断)。 在其他编程语言中,负值的转移权就像划分为舍入(朝向负无穷大)。
地址 Address
地址:保存一个20字节值(Ethereum地址的大小)。 地址类型也有成员,并作为所有合约的基础。
操作
-
<=
,<
,==
,!=
,>=
和>
地址成员 Members of Addresses
-
balance
和transfer
有关快速参考,请参阅地址相关。
可以使用财产余额查询地址的余额,并使用传输函数将以太网(以wei为单位)发送到地址:
address x = 0x123; address myAddress = this; if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);
如果x是合同地址,则其代码(更具体地说:其回退函数(如果存在))将与传送调用一起执行(这是EVM的限制,不能防止)。 如果执行任务无法运行,或以任何方式失败,则以太网传输将被恢复,并且当前的合同将以异常方式停止。
send
send是低级对等的转账。 如果执行失败,当前合同将不会以异常方式停止,但发送将返回false。
-
call
,callcode
anddelegatecall
此外,为了与不遵守ABI的合同进行接口,提供了任意数量的任意类型参数的函数调用。 这些参数被填充到32字节并连接。 一个例外是第一个参数被编码到正好四个字节的情况。 在这种情况下,这里没有填充以允许使用功能签名。
address nameReg = 0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2; nameReg.call("register","MyName"); nameReg.call(bytes4(keccak256("fun(uint256)")),a);
call
返回一个布尔值,指示调用的函数是否终止(true
)或引起EVM异常(false
)。 不可能访问返回的实际数据(为此,我们需要提前知道编码和大小)。
以类似的方式,可以使用函数delegatecall
:区别在于仅使用给定地址的代码,所有其他方面(存储,余额,…)均取自当前的合同。 委托人的目的是使用存储在另一个合同中的库代码。 用户必须确保两个合同中的存储布局适合委托使用。 在homestead之前,只有一个名为callcode
的有限变体才可用,不能访问原始的msg.sender
和msg.value
值。
所有三个功能call
,callcode
和delegatecall
都是非常低级的功能,只能作为最后的手段,因为它们打破了solidity的类型安全性。
.gas()
选项可用于所有三种方法,而delegatecall
不支持.value()
选项。
所有的合约都继承地址的成员,所以它是可以查询使用this.balance
当前合约的余额。
不鼓励使用callcode
,将来会被删除。
所有这些功能都是低级别的功能,应小心使用。 具体而言,任何未知的合约可能是恶意的,如果你调用它,你交出控制权,以该合同可能又回调到你的合约,所以要改变你的状态变量,当调用返回准备。
固定大小的字节数组
bytes1
,bytes2
,bytes3
,…,bytes32
。 byte
是bytes1
的别名。
操作:
- 比较:
<=
,<
,==
,!=
,>=
,>
(评估为bool) - 位运算符:
&
,|
,^
(按位异或),〜
(逐位否定),<<
(左移),>>
(右移) - 索引访问:如果x的类型为
bytesI
,则0 <= k <I
的x [k]
返回第k
个字节(只读)。
移位操作符以任何整数类型作为右操作数(但将返回左操作数的类型),表示要移位的位数。 移动负数将导致运行时异常。
成员:
.length
产生字节数组的固定长度(只读)。
动态大小的字节数组
bytes:
动态大小的字节数组,请参见数组。 不是价值型!
string:
动态尺寸的UTF-8编码字符串,请参见数组。 不是价值型!
作为一个经验法则,用于任意长度的原始字节数据和字符串的任意长度的字符串(UTF-8)数据字节。 如果可以将长度限制在一定数量的字节,那么请务必使用bytes1至bytes32之一,因为它们便宜得多。
固定点数 Fixed Point Numbers
固体点数目前尚未完全支持。 它们可以被声明,但不能被分配到或来自。
地址文字 Address Literals
通过地址校验和测试的十六进制文字,例如0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF是地址类型。 长度在39到41位之间的十六进制文字不通过校验和测试会产生警告,并被视为常规有理数字文字。
理性和整数文字 Rational and Integer Literals
整数文字由0-9范围内的数字序列组成。 它们被解释为小数。 例如,69意味着六十九。 八进制文字不存在于粘性和前导零无效。
小数部分文字由一个.
形成。 在一侧至少有一个数字。 例子包括1.
,.1
和1.3
。
也支持科学符号,其中基数可以分数,而指数不能。 实例包括2e10
,-2e10
,2e-10
,2.5e1
。
数字字面表达式保持任意精度,直到它们转换为非文字类型(即通过将它们与非文字表达式一起使用)。 这意味着计算不会溢出,而在数字文字表达式中,分割不会截断。
例如,(2**800 + 1) - 2**800
导致常数1
(类型uint8
),尽管中间结果甚至不适合机器字大小。 此外,.5 * 8
导致整数4
(尽管在其间使用非整数)。
只要操作数为整数,任何可应用于整数的运算符也可应用于数字文字表达式。 如果两者中的任何一个是分数的,则不允许位操作,如果指数是分数(因为可能导致非有理数),则不允许求幂)。
solidity有一些文本类型为每个有理数。 整数文字和理性数字字面值属于数字文字类型。 此外,所有数字字面值表达式(即仅包含数字文字和运算符的表达式)都属于数字字面值类型。 所以数字文字表达式1 + 2
和2 + 1
都属于理性数字3的相同数字字面值类型。
用于在早期版本中截断的整数文字的分割,但现在将转换为有理数,即5/2
不等于2
,但为2.5
因为它们与非文字表达式中使用的数字面表达式尽快转换成非文字类型。 尽管我们知道,在下面的例子中分配给b中的表达式的值的计算结果为一个整数,但局部表达2.5 + a
不类型检查所以代码不编译.
uint128 a = 1; uint128 b = 2.5 + a + 0.5;
字符串文字 String Literals
字符串文字用双引号或单引号("foo"
或''bar''
)编写。 它们并不意味着像C中那样为零; "foo"
代表三个不是四个字节。 与整数文字一样,它们的类型可以有所不同,但它们可以隐式转换为bytes1
,…,bytes32
(如果它们适合)到字节和字符串。
字符串文字支持转义字符,例如\n
,\xNN
和\uNNNN
。 \xNN
取十六进制值并插入相应的字节,而\ uNNNN
则使用Unicode代码点并插入UTF-8序列。
十六进制文字 Hexadecimal Literals
Hexademical Literals以关键字hex为前缀,以双引号或单引号(hex"001122FF"
)括起来。 它们的内容必须是十六进制字符串,它们的值将是这些值的二进制表示。
Hexademical Literals像字符串文字,具有相同的可转换性限制。
枚举
枚举是在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; } // 由于枚举类型不是ABI的一部分,所以对于solidity之外的所有事务,“getChoice”的签名将自动更改为“getChoice()return(uint8)”)。 所使用的整数类型足够大以容纳所有枚举值,即如果您有更多值,则将使用`uint16`等等。 function getChoice() returns (ActionChoices) { return choice; } function getDefaultChoice() returns (uint) { return uint(defaultChoice); } }
函数类型 Function Types
函数类型是函数的类型。 函数类型的变量可以从函数分配,函数类型的函数参数可以用于将函数传递给函数并从函数调用返回函数。 功能类型有两种功能 - 内部和外部功能:
内部函数只能在当前合约内部更详细地调用(更具体地说是在当前代码单元内部,也包括内部函数库和继承函数),因为它们不能在当前契约的上下文之外执行。 调用内部函数是通过跳转到其入口标签来实现的,就像在内部调用当前合同的函数一样。
外部功能由一个地址和一个函数签名,他们可以通过传递和外部函数调用返回。
功能类型记为如下:
function (<parameter types>) {internal|external} [pure|constant|view|payable] [returns (<return types>)]
function (<参数类型>) {internal|external} [pure|constant|view|payable] [returns (<返回类型>)]
与参数类型相反,返回类型不能为空 - 如果函数类型不返回任何东西,则returns (<return types>)
部分必须被省略。
缺省情况下,函数类型为internal
,内部关键字可以省略。 与此相反,合同函数本身默认为公用,只能作为类型的名称中使用时,默认为内部。
在当前合约中有两种访问函数的方法:直接以其名称,f
或使用this.f
. 前者将导致内部功能,后者在外部功能中。
如果函数类型变量未初始化,则调用它将导致异常。 如果在使用delete
之后调用函数也会发生这种情况。
如果在solidity的上下文中使用外部函数类型,则将它们视为函数类型,它以单个字节24类型将该地址后跟功能标识符编码在一起。
请注意,现行合约的公共函数既可以作为内部函数也可以用作外部函数。 要使用f
作为内部函数,只需使用f
,如果要使用其外部形式,请使用this.f
.
显示如何使用内部函数类型的示例:
pragma solidity ^0.4.5; library ArrayUtils { // 内部函数可以在内部函数库中使用,因为它们将成为相同代码上下文的一部分 //参数,整数数组、函数、内部的整数;内部;返回整数数组 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); //r为定义一个固定长度的数组 for (uint i = 0; i < r.length; i++) { r[i] = i; // 以此写入0 1 2 .. } } } //金字塔 contract Pyramid { using ArrayUtils for *; //参数,整形;返回整形 function pyramid(uint l) returns (uint) { //得到一个数组形如 arr = [0,1,2,3,4],各项的平方后返回新的数组,数组的各个元素相加求和 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) { // 这里检查答复是来自可信来源 requests[requestID].callback(response); //通过callback方法执行 } } //定义合约 contract OracleUser { Oracle constant oracle = Oracle(0x1234567); // 已知的合约 //调用 function buySomething() { //调用外部合约的方法,使用的第二个参数为当前合约的方法 oracle.query("USD",this.oracleResponse); } //参数为字节 function oracleResponse(bytes response) { require(msg.sender == address(oracle)); //如果消息发送的人和。。。相等 // Use the data } }
Lambda或内联函数已计划但尚未支持。
引用类型
复杂类型,即不总是适合256位的类型,必须比我们已经看到的值类型更仔细地处理。 由于复制它们可能相当昂贵,我们必须考虑我们是否希望将它们存储在内存中(不是持久存储的)或存储(保存状态变量的位置)。
数据位置 Data location
每个复杂类型,即数组和结构体,都有一个额外的注释,即“数据位置”,关于它是存储在内存中还是存储器中。 根据上下文,总是默认,但可以通过将存储或内存附加到类型来覆盖。 函数参数(包括返回参数)默认值是内存,局部变量默认是存储和位置被迫储存状态变量(显然)。
还有第三个数据位置calldata,它是一个不可修改的非持久性区域,其中存储了函数参数。 外部函数的函数参数(不返回参数)被强制调用数据,并且主要表现为内存。
因为他们改变了分配的行为数据的位置是很重要的:存储和内存,并以一个状态变量(甚至是从其他状态变量)之间的分配总是创建一个独立的副本。 本地存储变量的分配只能分配一个引用,而且该引用总是指向状态变量,即使后者在此同时被更改。 另一方面,从存储的存储引用类型到另一存储器引用类型的分配不会创建副本。
pragma solidity ^0.4.0; contract C { uint[] x; // x的数据位置是存储 // memoryArray的数据位置是内存 function f(uint[] memoryArray) { x = memoryArray; // 工作,将整个数组复制到存储 var y = x; // 工作,分配一个指针,y的数据位置是存储 y[7]; // fine,返回第8个元素 y.length = 2; // fine,通过y修改x delete x; // fine,清除数组,修改y // 以下不工作; 它将需要在存储中创建一个新的临时未命名数组,但存储是“静态”分配的: // y = memoryArray; //这也不行,因为它会“重置”指针,但没有明确的位置,它可以指向。 删除y; g(x); // 调用 g,移交到x的引用 h(x); // 调用 h ,在内存中创建一个独立的临时副本 } function g(uint[] storage storageArray) internal {} function h(uint[] memoryArray) {} }
总结:
强制数据位置:
- 外部函数的参数(不返回):calldata
- 状态变量:存储
默认数据位置:
- 函数的参数(也返回):内存
- 所有其他局部变量:存储
数组
数组可以具有编译时固定大小,也可以是动态的。 对于存储阵列,元素类型可以是任意的(也就是其他数组,映射或结构)。 对于存储器阵列,它不能是映射,如果它是公共可见函数的参数,则必须是ABI类型。
固定大小k和元素类型T的数组被写为T [k]
,动态大小的数组为T []
。 例如,uint的5个动态数组的数组是uint [] [5]
(注意,与其他语言相比,符号是相反的)。 要访问第三个动态数组中的第二个uint,您使用x [2] [1]
(索引为零,访问以相反的方式工作,即x [2]将类型中的一个级别 正确的)。
类型字节和字符串的变量是特殊的数组。 一个字节类似于byte [],但是它被紧密地打包在calldata中。 字符串等于字节,但不允许长度或索引访问(现在)。
因此,字节应该始终优先于byte [],因为它成本更低。
如果要访问字符串s
的字节表示,请使用bytes(s).length
/ bytes(s)[7] = ''x''
;. 请记住,您正在访问UTF-8表示的低级字节,而不是单个字符!
可以将数组标记为public,并且solidity创建一个getter。 数字索引将成为getter必需的参数。
分配内存数组 Allocating Memory Array
在内存中创建可变长度的数组可以使用new
关键字来完成。 与存储阵列相反,不能通过分配给.length
成员来调整存储器阵列的大小。
pragma solidity ^0.4.0; contract C { function f(uint len) { uint[] memory a = new uint[](7); //第7个 bytes memory b = new bytes(len); // Here we have a.length == 7 and b.length == len a[6] = 8; } }
数组文字/内联数组 Array Literals / Inline Arrays
数组字面值是写入表达式的数组,不会立即分配给变量。
pragma solidity ^0.4.0; contract C { function f() { g([uint(1),2,3]); } function g(uint[3] _data) { // ... } }
数组文字的类型是固定大小的内存数组,其基类型是给定元素的常见类型。 [1,2,3]的类型是uint8 [3]内存,因为这些常量的类型是uint8。 因此,有必要将上述示例中的第一个元素转换为uint。 请注意,目前,固定大小的存储阵列不能分配给动态大小的存储器阵列,即不可能实现以下功能:
// 这不会编译。 pragma solidity ^0.4.0; contract C { function f() { //下一行创建一个类型错误,因为uint [3]内存 //无法转换为uint []内存。 uint[] x = [uint(1),3,4]; } }
计划在将来删除这个限制,但由于在ABI中如何传递数组,因此目前还会产生一些并发症。
成员
length:
数组有一个长度成员来保存它们的元素数量。 可以通过更改.length成员来调整动态数组的大小(不在内存中)。 尝试访问当前长度之外的元素时,不会自动发生。 一旦创建了存储器阵列的大小是固定的(但是动态的,即它可以依赖于运行时参数)。
push:
动态存储阵列和字节(不是字符串)具有称为push的成员函数,可用于在数组的末尾追加元素。 该函数返回新的长度。
在外部函数中不可能使用阵列数组。
由于EVM的限制,不可能从外部函数调用返回动态内容。 contract C { function f() returns (uint[]) { ... }
将返回一个如果从web3.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存储在内存中 - 函数参数的默认值 function setAllFlagPairs(bool[2][] newPairs) { // 分配到一个存储阵列替换整个阵列 m_pairsOfFlags = newPairs; } function setFlagPair(uint index,bool flagA,bool flagB) { // 访问不存在的索引将抛出异常 m_pairsOfFlags[index][0] = flagA; m_pairsOfFlags[index][1] = flagB; } function changeFlagArraySize(uint newSize) { // 如果新的大小较小,则删除的数组元素将被清除 m_pairsOfFlags.length = newSize; } function clear() { // 这些完全清除了阵列 delete m_pairsOfFlags; delete m_aLotOfIntegers; // 相同的效果在这里 m_pairsOfFlags.length = 0; } bytes m_byteData; function byteArrays(bytes data) { // 字节数组(“bytes”)是不同的,因为它们在没有填充的情况下被存储,但可以被视为与“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) { // 动态内存数组使用`new`创建: uint[2][] memory arrayOfPairs = new uint[2][](size); // 创建一个动态字节数组: bytes memory b = new bytes(200); for (uint i = 0; i < b.length; i++) b[i] = byte(i); return b; } }
结构体 Structs
solidity提供了一种以结构体形式定义新类型的方法,如下例所示:
pragma solidity ^0.4.11; contract CrowdFunding { // 定义一个类型有两个字段 struct Funder { address addr; uint amount; } struct Campaign { address beneficiary; uint fundingGoal; uint numFunders; uint amount; mapping (uint => Funder) funders; } uint numCampaigns; mapping (uint => Campaign) campaigns; function newCampaign(address beneficiary,uint goal) returns (uint campaignID) { campaignID = numCampaigns++; // campaignID 是返回值 // 重新建立新的结构并保存在存储中。 我们忽略了映射类型。 campaigns[campaignID] = Campaign(beneficiary,goal,0,0); } function contribute(uint campaignID) payable { Campaign storage c = campaigns[campaignID]; // 创建一个新的临时内存结构,用给定值初始化并将其复制到存储。 //你也可以通过 Funder(msg.sender,msg.value) 进行初始化 c.funders[c.numFunders++] = Funder({addr: msg.sender,amount: msg.value}); c.amount += msg.value; } function checkGoalReached(uint campaignID) returns (bool reached) { Campaign storage c = campaigns[campaignID]; if (c.amount < c.fundingGoal) return false; uint amount = c.amount; c.amount = 0; c.beneficiary.transfer(amount); return true; } }
合约没有提供众筹合约的全部功能,但它包含理解结构所必需的基本概念。 结构类型可以在映射和数组中使用,它们本身可以包含映射和数组。
结构体不可能包含自己类型的成员,尽管struct本身可以是映射成员的值类型。 这个限制是必要的,因为结构的大小必须是有限的。
请注意,在所有函数中,将struct类型分配给局部变量(默认存储数据位置)。 这不会复制结构体,而只存储一个引用,以便对局部变量的成员的赋值实际写入状态。
当然,您也可以直接访问结构的成员,而不必将其分配给本地变量,如广告系列[campaignID] .amount = 0。
映射 Mappings
映射类型被声明为mapping(_KeyType => _ValueType)
。 这里_KeyType
可以是几乎任何类型,除了映射,动态大小的数组,契约,枚举和结构体。 _ValueType
实际上可以是任何类型,包括映射。
映射可以看作是虚拟初始化的哈希表,使得每个可能的键都存在,并被映射到一个值,其字节表示全为零:一个类型的默认值。 相似之处在此结束,但是:密钥数据实际上并不存储在映射中,只有其keccak256哈希用于查找该值。
因此,映射没有长度或概念的键或值被设置。
映射只允许用于状态变量(或内部函数中的存储引用类型)。
有可能public
标记映射,并具有solidity创建一个getter。 _KeyType将成为getter的必需参数,它将返回_ValueType。
_ValueType也可以是映射。 getter将递归地为每个_KeyType设置一个参数。
pragma solidity ^0.4.0; contract MappingExample { mapping(address => uint) public balances; function update(uint newBalance) { balances[msg.sender] = newBalance; } } contract MappingUser { function f() returns (uint) { MappingExample m = new MappingExample(); m.update(100); return m.balances(this); } }
操作者涉及LValues Operators Involving LValues
如果a
是一个LValue(即,可以分配给一个变量或东西),下面的运算符可作为简写:
a += e
等价于a = a + e
。 相应地定义运算符 -=
,*=
,/=
,%=
,a |=
,&=
和^=
。 a++
和a--
等价于a += 1
/ a -= 1
,但表达式本身仍然具有以前的值a
。 相反,--a
和++ a
对于a而言具有相同的效果,但在更改后返回值。
delete
delete a
将类型的初始值分配给a。即 对于整数,它等效于a = 0,但它也可以用于数组,其中分配长度为零的动态数组或与所有元素重置的长度相同的静态数组。 对于结构体,它会为所有成员重新分配一个结构体。
删除对整个映射没有影响(因为映射的关键字可能是任意的,并且通常是未知的)。 所以,如果你删除一个结构体,它将会重置所有不是映射的成员,也可以递归到成员中,除非是映射。 但是,可以删除单个键及其映射到的内容。
pragma solidity ^0.4.0; contract DeleteExample { uint data; uint[] dataArray; function f() { uint x = data; delete x; // 将x设置为0,不影响数据 delete data; // 将数据设置为0,不影响仍然保存副本的x uint[] y = dataArray; delete dataArray; // 这将dataArray.length设置为零,但是由于uint []是一个复杂对象,所以受影响也是存储对象的别名。另一方面:“delete y”无效,因为引用存储的本地变量的赋值 对象只能由现有的存储对象进行。 } }
基本类型之间的转换
隐性转换
如果运算符应用于不同类型,编译器将尝试将其中一个操作数隐式转换为其他类型(对于赋值也是如此)。 一般来说,值类型之间的隐式转换是有可能的,如果它在语义上有意义且没有信息丢失:uint8可以转换为uint16和int128到int256,但int8不能转换为uint256(因为uint256不能保持为-1)。 此外,无符号整数可以转换为相同或更大尺寸的字节,但反之亦然。 任何可以转换为uint160的类型也可以转换为地址。
显式转换
如果编译器不允许隐式转换,但是你知道你正在做什么,那么有时可以使用显式类型转换。 请注意,这可能会给您一些意想不到的行为,所以一定要测试,以确保结果是你想要的! 以下示例将负的int8转换为uint:
int8 y = -3; uint x = uint(y);
在这段代码片段末尾,x将具有0xfffff..fd(64个十六进制字符)的值,在256位的二进制补码表示中为-3。
如果一个类型被明确转换为较小的类型,则高阶位被切断:
uint32 a = 0x12345678; uint16 b = uint16(a); // b will be 0x5678 Now
类型推导
为方便起见,并不总是需要明确指定变量的类型,编译器会根据分配给该变量的第一个表达式的类型自动推断:
uint24 x = 0x123; var y = x;
这里,y的类型将是uint24。 对于函数参数或返回参数,不能使用var。
该类型仅从第一个赋值中推导出来,所以以下代码段中的循环是无限的,因为我将具有类型uint8,并且此类型的任何值都小于2000.对于for (var i = 0; i < 2000; i++) { ... }
根据例子学习Solidity
声明:本系列文章是自己在http://solidity-cn.readthedoc... 学习solidity时,因为英语水平不够,被迫用谷歌粗略翻译的。仅为了方便学习,中间肯定有很多错误的地方。请勘误。
本文原地址:http://solidity-cn.readthedoc...
以下是翻译正文:
投票:
以下合同非常复杂,但展示了很多Solidity的功能。 它执行投票合同。 当然,电子投票的主要问题是如何为正确的人员分配投票权,以及如何防止操纵。 我们不会在这里解决所有问题,但至少我们会展示如何进行委派投票,以便计票自动且完全透明。
这个想法是为每个选票创建一个合同,为每个选项提供一个简称。 然后,担任主席的合同创建者将分别给予每个地址的投票权。
然后,地址背后的人可以选择自己投票,或者将他们的投票委托给他们信任的人。
在投票时间结束时,winningProposal()
将返回投票数最多的提案。
pragma solidity ^0.4.21
/// @title Voting with delegation.
contract Ballot {
//这将声明一个新的复合类型
//稍后用于变量。
//它会代表一个voter。
struct Voter {
uint weight; //weight is accumulated by delegation
bool voted; // 如果true,已经voted
address delegate; // 被授权的人
uint vote; // vote提案的索引
}
// a type for a single proposal.
struct Proposal {
bytes32 name;
uint voteCount;
}
address public chairperson;
//声明一个状态变量,为每个可能的地址存储一个`Voter`结构体。
mapping(address => Voter) public voters;
// Proposal结构的动态大小的数组。
Proposal[] public proposals;
///创建一个新ballot来选择`proposalNames`中的一个。
function Ballot(bytes32[] proposalNames) public {
chairperson = msg.sender;
voters[chairperson].weight = 1;
//对于提供的每个提议名称,创建一个新的提议对象并将其添加到数组的末尾。
for (uint i = 0, i < proposalNames.length; i++) {
//`Proposal({...})`创建一个临时的Proposal对象,
// `proposals.push(...)`将它附加到`proposals`的末尾
proposals.push(Proposal({
name:proposalNames[i];
voteCount:0
}));
}
}
//让`voter`有权对这张选票进行投票。
//只能由`chairperson`调用。
function giveRightToVote(address voter) public {
//如果`require`的参数评估为''false'',
//它会终止并恢复对状态和以太平衡的所有更改。 如果函数被错误地调用,
//通常使用它是一个好方法。 但要小心,这也将消耗所有提供的gas(这是计划在未来改变)。
require((msg.sender == chairperson) && !voters[voter].voted && (voters[voter].weight == 0))
voters[voter].weight = 1;
}
function delegate(address to) public {
Voter storage sender = voters[msg.sender];
require(!sender.voted);
require(to != msg.sender);
// 一般来说,这样的循环是非常危险的,因为如果它们运行时间太长,
// 它们可能需要比块中可用的更多的气体。在这种情况下,委托将不会被执行,
// 但在其他情况下,这样的循环可能导致合同 完全“卡住”。
while (voters[to].delegate != address(0)) {
to = voters[to].delegate;
require(to != msg.sender);
}
//因为`sender`是一个引用,所以这会修改`voters[msg.sender].voted`
sender.voted = true;
sender.delegate = to;
Voter storage delegate = voters[to];
if(delegate.voted) {
//如果委托人已投票,则直接添加投票数
proposals[delegate.vote].voteCount += sender.weight;
} else {
//如果代表尚未投票,增加
delegate.weight += sender.weight;
}
}
///将您的投票(包括授予您的投票)提交给提案[proposal] .name`。
function vote(uint proposal) public {
Voter storage sender = voters[msg.sender];
require(!sender.voted);
sender.voted = true;
sender.vote = proposal;
//如果`proposal''超出了数组范围,这将自动抛出并恢复所有更改。
proposals[proposal].voteCount += sender.weight;
}
/// @dev计算以前所有投票的获胜建议。
function winningProposal() public view returns (uint winningProposal) {
uint winningVoteCount = 0;
for (uint p = 0, p < proposals.length; p++) {
if (proposals[p].voteCount > winningVoteCount) {
winningVoteCount = proposals[p].voteCount;
winningProposal = p;
}
}
}
//调用winningProposal()函数获取提议数组中包含的获奖者的索引,然后返回获胜者的名字
function winnerName() public view
returns (bytes32 winnerName)
{
winnerName = proposals[winningProposal()].name;
}
}
可能的改进
目前,需要许多交易来将投票权分配给所有参与者。 你能想出更好的方法吗?
秘密竞价(盲拍)
在本节中,我们将展示在以太坊创建一个完全失明的拍卖合同是多么容易。 我们将从公开拍卖开始,每个人都可以看到所做的投标,然后将此合同扩展到盲目拍卖,在竞标期结束之前无法看到实际出价。
简单的公开拍卖
以下简单的拍卖合同的总体思路是每个人都可以在投标期内发送他们的出价。 出价已经包括发送金钱/以太币以使投标人与他们的出价相结合。 如果提高最高出价,以前出价最高的出价人可以拿回她的钱。 在投标期结束后,合同必须手动为受益人接收他的钱, 合同不能激活自己。
pragma solidity ^0.4.21
contract SimpleAuction {
//拍卖的参数。 时间是绝对的unix时间戳
//(自1970-01-01以来的秒数)或以秒为单位的时间段。
address public beneficiary;
uint public auctionEnd;
// 拍卖当前的状态
address public highestBidder;
uint public highestBid;
//允许撤回之前的出价
mapping(address => uint) pendingReturns;
// 设置为true,禁止任何更改
bool ended;
// 将在更改中触发的事件。
event HighestBidIncreased(address bidder, uint amount);
event AuctionEnded(address winner, uint amount);
//以下是所谓的natspec评论,可以通过三个斜杠来识别。
//当用户被要求确认交易时将显示。
///代表受益人地址`_beneficiary`以`_biddingTime`秒的投标时间创建一个简单的拍卖。
function SimpleAuction(
uint _biddingTime,
address _beneficiary
) public {
beneficiary = _beneficiary;
auctionEnd = now + _biddingTime;
}
///使用与此交易一起发送的价格拍卖拍卖品。 如果拍卖没成功,价值只会被退还。
function bid() public payable {
//不需要参数,所有信息已经是交易的一部分。 为了能够接收以太网,功能需要关键字。
//如果结束,请恢复通话。
require(now <= auctionEnd);
// 如果出价不高,将钱退回。
require(msg.value > highestBid);
if (highestBid != 0) {
//通过使用highestBidder.send(highestBid)发送回款有安全风险,
// 因为它可以执行不可信的合同。
// 让收款人自己收回钱会比较安全。
pendingReturns[highestBidder] += highestBid;
}
highestBidder = msg.sender;
highestBid = msg.value;
HighestBidIncreased(msg.sender, msg.value);
}
///撤销高出价的出价。
function withdraw() public returns (bool) {
uint amount = pendingReturns[msg.sender];
if (amount > 0) {
//将它设置为零是很重要的,因为收件人可以在`send`返回
// 之前再次调用此函数作为接收调用的一部分。
pendingReturns[msg.sender] = 0;
if (!msg.sender.send(amount)) {
//不需要在这里呼叫,只需重新设置欠款额
pendingReturns[msg.sender] = amount;
return false;
}
}
return true;
}
///结束拍卖并将最高出价发送给受益人。
function auctionEnd() public {
//将与其他合约交互的函数(即它们调用函数或发送Ether)结构化为三个阶段是一个很好的指导:
// 1.检查条件
// 2.执行操作(潜在的变化条件)
// 3.与其他合同交互
//如果这些阶段混淆在一起,另一个合约可以回拨到当前合约中,并修改多次执行的状态或原因效果(以太付款).
//如果内部调用的函数包含与外部合同的交互,则必须将它们视为与外部合同的交互。
// 1.条件
require(now >= auctionEnd); //拍卖还没有结束
require(!ended); //此功能已被调用
// 2.效果
ended = true;
AuctionEnded(highestBidder, highestBid);
// 3.交互
beneficiary.transfer(highestBid);
}
}
盲拍
以前的公开拍卖会延伸到以下的盲拍。盲拍的优势在于投标期结束时没有时间压力。在一个透明的计算平台上创建一个盲目拍卖可能听起来像是一个矛盾,但是密码学可以解决这个问题。
在投标期间,投标人实际上并没有发出她的投标,而只是一个散列版本。由于目前认为实际上不可能找到两个(足够长)的哈希值相等的值,因此投标人承诺通过该投标。投标结束后,投标人必须公开他们的投标:他们将他们的价值未加密并且合同检查散列值与投标期间提供的散列值相同。
另一个挑战是如何在同一时间使拍卖具有约束力和盲目性:在赢得拍卖后,防止投标人不发送货币的唯一方法是让她在拍卖中一并发送。由于价值转移不能在以太坊蒙蔽,任何人都可以看到价值。
以下合同通过接受任何大于最高出价的值来解决此问题。因为这当然只能在披露阶段进行检查,所以有些出价可能是无效的,这是有意的(它甚至提供了一个明确的标记,用高价值转让放置无效出价):投标人可以通过放置几个较高的低无效出价。
pragma solidity ^0.4.21
contract BlindAuction {
struct Bid {
bytes32 blindedBid;
uint deposit;
}
address public beneficiary;
uint public biddingEnd;
uint public revealEnd;
bool public ended;
mapping(address => Bid[]) public bids;
address public highestBidder;
uint public highestBid;
//允许撤回之前的出价
mapping(address => uint) pendingReturns;
event AuctionEnded(address winnder, uint highestBid);
///修饰符是验证函数输入的便捷方式。 `onlyBefore`应用于下面的`bid`:
// 新函数体是修饰符的主体,其中`_`被旧函数体替换。
modifier onlyBefore(uint _time) { require(now < _time); _; }
modifier onlyAfter(uint _time) { require(now > _time); _; }
function BlindAuction {
uint _biddingTime,
uint _revealTime,
address _beneficiary
} public {
beneficiary = _beneficiary;
biddingEnd = now + _biddingTime;
revealEnd = biddingEnd + _revealTime;
}
///用`_blindedBid` = keccak256(value,fake,secret)放置一个不知情的出价。
///如果投标在披露阶段正确显示,则只会退还已发送的以太币。
//如果与投标一起发送的以太币至少“value”和“fake”不是true,则投标有效。
//将“fake”设置为true,并发送不确切的金额是隐藏实际出价但仍然需要存款的方法。 同一个地址可以放置多个出价。
function bid(bytes32 _blindedBid) public payable onlyBefore(biddingEnd)
{
bids[msg.sender].push(Bid({
blindedBid: _blindedBid,
deposit: msg.value
}));
}
///揭示你不知情的投标。 对于所有正确无视的无效出价以及除最高出价以外的所有出价,您都将获得退款。
function reveal (
uint[] _values,
bool[] _fake,
bytes32[] _secret
) public onlyAfter(biddingEnd) onlyBefore(revealEnd)
{
uint length = bids[msg.sender].length;
require(_values.length == length);
require(_fake.length == length);
require(_secret.length == length);
uint refund;
for (uint i = 0; i < length; i++) {
var bid = bids[msg.sender][i];
var (value, fake, secret) = (_values[i], _fake[i], _secret[i]);
if (bid.blindedBid != keccak256(value,fake,secret)) {
//投标并未实际显示。
//不要押金。
continue;
}
refund += bid.deposit;
if (!fake && bid.deposit > value) {
if (placeBid(msg.sender, value)) refund -= value;
}
//使发件人无法重新申请相同的存款。
bid.blindedBid = bytes32(0);
}
msg.sender.transfer(refund);
}
//这是一个“内部”功能,这意味着它只能从合同本身(或衍生合同)中调用。
function placeBid(address bidder, uint value) internal return (bool success)
{
if (value <= highestBid) {
return false;
}
if (highestBidder != 0) {
//退还先前出价最高的出价者。
pendingReturns[highestBidder] += highestBid;
}
highestBid = value;
highestBidder = bidder;
return true;
}
///撤销高出价的出价。
function withdraw() public {
uint amount = pendingReturns[msg.sender];
if (amount > 0) {
//将它设置为零是很重要的,因为接收者可以在`transfer`返回之前再次调用
//此函数作为接收调用的一部分(请参阅上面关于条件 - >效果 - >交互的注释)。
pendingReturns[msg.sender] = 0;
msg.sender.transfer(amount);
}
}
///结束拍卖并将最高出价发送给受益人。
function auctionEnd () public onlyAfter(revealEnd)
{
require(!ended);
AuctionEnded(highestBidder, highestBid);
ended = true;
beneficiary.transfer(highestBid);
}
}
安全的远程购买
pragma solidity ^0.4.21
contract Purchase {
uint public value;
address public seller;
address public buyer;
enum State { Created, Locked, Inactive}
State public state;
//确保`msg.value`是一个偶数。
//如果它是一个奇数,则它将被截断。
//通过乘法检查它不是奇数。
function Purchase() public payable {
seller = msg.sender;
value = msg.value / 2;
require((2 * value) == msg.value);
}
modifier condition(bool _condition) {
require(_condition);
_;
}
modifier onlyBuyer() {
require(msg.sender == buyer);
_;
}
modifier onlySeller() {
require(msg.sender == seller);
_;
}
modifier inState(State _state) {
require(state == _state);
_;
}
event Aborted();
event PurchaseConfirmed();
event ItemReceived();
///中止购买并回收以太。
///只能在合同被锁定之前由卖家调用。
function abort() public onlySeller inState(State.Created)
{
Aborted();
state = State.Inactive;
seller.transfer(this.balance);
}
///将购买确认为买家。
/// Transaction必须包含`2 * value`以太。
///以太会被锁定,直到confirmReceived被调用。
function confirmPurchase() public inState(State.Created) condition(msg.value == (2 * value)) payable
{
PurchaseConfirmed();
buyer = msg.sender;
state = State.Locked;
}
///确认您(买家)收到该物品。
///这将释放锁定的以太。
function confirmReceived() public onlyBuyer inState(State.Locked)
{
ItemReceived();
//先改变状态很重要,否则使用下面的`send`调用的合约可以在这里再次调用。
state = State.Inactive;
//NOTE:这实际上允许买家和卖家阻止退款 - 应该使用退款模式。
buyer.transfer(value);
seller.transfer(this.balance);
}
}
微支付通道
To be written.
深入理解Solidity之二---Solidity源代码文件结构
Solidity源代码文件结构
源文件可以包含任意数量的合约定义,包括指令和编译指示。
版本Pragma
源文件可以(也应该)用所谓的版本注释来注释,以拒绝被编译为未来可能引入不兼容更改的编译器版本。 我们试图将这种变化保持在绝对最低限度,特别是引入变化的方式是语义的变化也需要语法的变化,但这当然不总是可能的。 因此,至少对于包含重大更改的版本,通读更新日志总是一个好主意,这些版本始终具有0.x.0或x.0.0格式的版本。
版本附注使用如下:
pragma solidity ^0.4.0;
这样的源代码文件不会使用早于版本0.4.0的编译器进行编译,并且它也不适用于从版本0.5.0开始的编译器(第二个条件是使用^添加的)。 这背后的想法是,在版本0.5.0之前不会有任何重大更改,所以我们始终可以确定我们的代码将按照我们打算的方式进行编译。 我们不修复编译器的确切版本,因此bug修复版本仍然有可能。
可以为编译器版本指定更复杂的规则,表达式遵循npm使用的规则。
导入其他源文件
语法和语义
Solidity支持非常类似于JavaScript中可用的导入语句(来自ES6),尽管Solidity不知道“默认导出”的概念。
在全局范围内,您可以使用以下格式的导入语句:
import "filename";
该语句从“文件名”(及其导入的符号)中导入所有全局符号到当前全局作用域(与ES6不同,但向后兼容Solidity)。
import * as symbolName from "filename";
...创建一个新的全局符号symbolName
,其成员全部来自“filename
”的全局符号。
import {symbol1 as alias, symbol2} from "filename";
...分别创建新的全局符号alias
和symbol2
,它们分别从“filename
”引用symbol1
和symbol2
。
另一种语法不是ES6的一部分,但可能很方便:
import "filename" as symbolName;
这相当于从import * as symbolName from "filename";
。
Paths
在上面,filename
总是被视为一个路径,其中/
作为目录分隔符。.
作为当前和..
作为父目录。 什么时候 .
或..
后跟一个除/
以外的字符,它不被视为当前或父目录。 所有路径名都被视为绝对路径,除非它们以当前的.
或父目录开头..
。
要从与当前文件相同的目录中导入文件x
,请使用import "./x" as x;
. 如果使用import "x" as x;
则可以引用不同的文件(在全局“包含目录”中)。
它取决于编译器(见下文)如何实际解析路径。 通常,目录层次不需要严格映射到你的本地文件系统,它也可以映射到通过例如发现的资源。 ipfs,http或者git。
在实际编译器中使用
调用编译器时,不仅可以指定如何发现路径的第一个元素,但可以指定路径前缀重新映射,以便例如 github.com/ethereum/dapp-bin/library
被重新映射到/usr/local/dapp-bin/library
,编译器将从那里读取文件。 如果可以应用多重重映射,则首先尝试使用最长密钥的那个。 这允许用例如fallback-remapping
""
映射到/usr/local/include/solidity
(原文:This allows for a "fallback-remapping" with e.g. "" maps to
> "/usr/local/include/solidity") 翻译的不太通顺。
。 此外,这些重新映射可能取决于上下文,从而允许您配置要导入的包。 不同版本的同名图书馆。
solc:
对于solc(命令行编译器),这些重新映射是作为context:prefix = target
参数提供的,其中context:
和= target
部分都是可选的(在这种情况下目标缺省为前缀)。 所有重映射值都是常规文件(包括它们的依赖关系)。 这种机制是完全向后兼容的(只要没有文件名包含=或:),因此不是一个突破性的改变。 在导入以prefix
开头的文件的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的模块。 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
中的导入将获得新版本。
请注意,solc仅允许您包含来自特定目录的文件:它们必须位于某个明确指定的源文件的目录(或子目录)中,或位于重新映射目标的目录(或子目录)中。 如果你想允许直接绝对包含,只需添加重新映射=/
。
如果存在多个导致有效文件的重映射,则选择具有最长公共前缀的重映射。
Remix:
Remix为github提供了一个自动重新映射,并且还会自动通过网络检索文件:您可以通过导入可迭代映射例如:
import "github.com/ethereum/dapp-bin/library/iterable_mapping.sol" as it_mapping;
未来可能会添加其他源代码提供者。
Comments
单行注释(//
)和多行注释(/*...*/
)是可用的。
// This is a single-line comment.
/*
This is a
multi-line comment.
*/
此外,还有另一种类型的注释称为natspec注释,对此文档尚未编写。 它们用三斜杠(///
)或双星号块(/ ** ... * /
)编写,它们应该直接用在函数声明或语句之上。 您可以在这些注释中使用Doxygen风格的标签来记录函数,为形式验证注释条件,并提供确认文本,当用户尝试调用函数时向用户显示。
在下面的例子中,我们记录了合同的标题,两个输入参数和两个返回值的解释。
pragma solidity ^0.4.0;
/** @title Shape calculator. */
contract shapeCalculator {
/** @dev Calculates a rectangle''s surface and perimeter.
* @param w Width of the rectangle.
* @param h Height of the rectangle.
* @return s The calculated surface.
* @return p The calculated perimeter.
*/
function rectangle(uint w, uint h) returns (uint s, uint p) {
s = w * h;
p = 2 * (w + h);
}
}
Solidity 中的基本类型转换
Solidity 中的基本类型转换(十四)| 入门系列 2017/4/29 posted in Solidity 入门系列 点击查看原文,获得优化的排版。
- 隐式转换 如果一个运算符能支持不同类型。编译器会隐式的尝试将一个操作数的类型,转为另一个操作数的类型,赋值同理。
一般来说,值类型间的互相转换只要不丢失信息,语义可通则可转换。下面,我们来看一个整数转换的例子:
pragma solidity ^0.4.0;
contract Int {function conversion () returns (uint16){ uint8 a = 1; // 隐式转换 uint16 b = a;
return (b);
} } 上面的例子中,我们将一个 uint8 的变量 a 隐式的转换为了 uint16。同理它还支持转为 uint32,uint128 和 uint256。
另外,无符号整数可以被转为同样,或更大的字节的类型。但需要注意的是,不能反过来转换。由于 address 是 20 字节大小,所以它与 int160 大小是一样。
pragma solidity ^0.4.0;
contract IntToAddress {function f () returns (uint){ uint160 i = 10; address addr = i; return addr.balance; } } 上面的例子中,将 uint160 的 i 转为了一个 address。
- 显式转换 编译器不会将语法上不可转换的类型进行隐式转换,此时我们要通过显式转换的方式,比如将一个有符号整数,转为一个无符号整数。
pragma solidity ^0.4.0;
contract ExplicitConversion{ function f() returns (int8){ uint8 a = 1;
//强制转换
int8 b = int8(a);
return b;
} } 3. 类型推断 有时为了方便,我们不会显式定义类型。但由于编译器,会自动挑选一个最恰当的类型,所以会常常留下坑,我们来看这个例子:
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; } } 大家可以想想上述代码运行的结果。
上述代码运行的结果实际为 2100。原因是因为 var i = 0 定义时,通过类型推断,i 的实际类型为 uint8,所以它会一直循环,如果没有 count >= 2100 这个判断语句,这个循环将永远不会结束。
- 一些常见的转换方案 uint 转为 bytes 将一个 uint 转转 bytes,可以使用 assembly1。
function toBytes (uint256 x) returns (bytes b) { b = new bytes (32); assembly { mstore (add (b, 32), x) } } 上面的转换方式可能是效率最高的方式。
string 转为 bytes string 可以显示的转为 bytes。但如果要转为 bytes32,可能只能使用 assembly2。
pragma solidity ^0.4.0;
contract StringToBytes{ function StringToBytesVer1(string memory source) returns (bytes result) { return bytes(source); }
function stringToBytesVer2(string memory source) returns (bytes32 result) { assembly { result := mload(add(source, 32)) } } }
Solidity 官方文档中文版 3_安装Solidity
基于浏览器的Solidity
如果你只是想尝试一个使用Solidity的小合约,你不需要安装任何东西,只要访问 基于浏览器的Solidity http://remix.ethereum.org/。
如果你想离线使用,你可以保存页面到本地,或者从 http://github.com/chriseth/browser-solidity 克隆一个。
NPM / node.js
这可能安装Solidity到本地最轻便最省事的方法。
在基于浏览器的Solidity上,Emscripten提供了一个跨平台JavaScript库,把C++源码编译为JavaScript,同时也提供NPM安装包。
去安装它就可以简单使用。,
npm install solc
如何使用nodejs包的详细信息可以在代码库中找到.
二进制安装包
Ethereum.
包括Mix IDE的二进制Solidity安装包在Ethereum网站C++ bundle中下载。
从源码构建
在MacOS X、Ubuntu和其它类Unix系统中编译安装Solidity非常相似。这个指南开始讲解如何在每个平台下安装相关的依赖软件,然后构建Solidity。
MacOS X
系统需求:
-
OS X Yosemite (10.10.5)
-
Homebrew
- Xcode
安装Homebrew:
brew update
brew install boost --c++11 # 这需要等待一段时间
brew install cmake cryptopp miniupnpc leveldb gmp libmicrohttpd libjson-rpc-cpp
# 仅仅安装Mix IDE和Alethzero
brew install xz d-bus
brew install llvm --HEAD --with-clang
brew install qt5 --with-d-bus # 如果长时间的等待让你发疯,那么添加--verbose输出信息会让你感觉更好。
Ubuntu 系统
下面是在最新版Ubuntu系统上编译安装Solidity的指南。最佳的支持平台是2014年11月发布的64位Ubuntu 14.04,至少需要2GB内存。我们所有的测试都是基于此版本,当然我们也欢迎其它版本的测试贡献者。
安装依赖软件:
在你从源码编译之前,你需要准备一些工具和依赖软件。
首先,升级你的代码库。Ubuntu主代码库不提供所有的包,你需要从Ethereum PPA和LLVM获取。
注意
Ubuntu 14.04的用户需要使用:sudo apt-add-repository ppa:george-edison55/cmake-3.x
获取最新版本的cmake。
现在加入其它的包:
sudo apt-get -y update
sudo apt-get -y install language-pack-en-base
sudo dpkg-reconfigure locales
sudo apt-get -y install software-properties-common
sudo add-apt-repository -y ppa:ethereum/ethereum
sudo add-apt-repository -y ppa:ethereum/ethereum-dev
sudo apt-get -y update
sudo apt-get -y upgrade
对于Ubbuntu 15.04(Vivid Vervet)或者更老版本,使用下面的命令获取开发相关的包:
sudo apt-get -y install build-essential git cmake libboost-all-dev libgmp-dev libleveldb-dev libminiupnpc-dev libreadline-dev libncurses5-dev libcurl4-openssl-dev libcryptopp-dev libjson-rpc-cpp-dev libmicrohttpd-dev libjsoncpp-dev libedit-dev libz-dev
对于Ubbuntu 15.10(Wily Werewolf)或者更新版本,使用下面的命令获取开发相关的包:
sudo apt-get -y install build-essential git cmake libboost-all-dev libgmp-dev libleveldb-dev libminiupnpc-dev libreadline-dev libncurses5-dev libcurl4-openssl-dev libcryptopp-dev libjsonrpccpp-dev libmicrohttpd-dev libjsoncpp-dev libedit-dev libz-dev
不同版本使用不同获取命令的原因是,libjsonrpccpp-dev已经在最新版的Ubuntu的通用代码仓库中。
编译
如果你只准备安装solidity,忽略末尾Alethzero和Mix的错误。
git clone --recursive https://github.com/ethereum/webthree-umbrella.git
cd webthree-umbrella
./webthree-helpers/scripts/ethupdate.sh --no-push --simple-pull --project solidity
# 更新Solidity库
./webthree-helpers/scripts/ethbuild.sh --no-git --project solidity --all --cores 4 -DEVMJIT=0
# 编译Solidity及其它
# 在OS X系统加上DEVMJIT将不能编译,在Linux系统上则没问题
如果你选择安装Alethzero和Mix:
git clone --recursive https://github.com/ethereum/webthree-umbrella.git
cd webthree-umbrella && mkdir -p build && cd build
cmake ..
如果你想帮助Solidity的开发,你需要分支(fork)Solidity并添加到你的私人远端分支:
cd webthree-umbrella/solidity
git remote add personal git@github.com:username/solidity.git
注意webthree-umbrella使用的子模块,所以solidity是其自己的git代码库,但是他的设置不是保存在 .git/config
, 而是在webthree-umbrella/.git/modules/solidity/config
.
关于【Solidity】3.类型 - 深入理解Solidity和solidity enum的问题就给大家分享到这里,感谢你花时间阅读本站内容,更多关于
本文标签: