写在前面:HiBlock区块链社区成立了翻译小组,翻译区块链相关的技术文档及资料,本文为Solidity文档翻译的第十三部分《贡献方式及常见问题》,特发布出来邀请solidity爱好者、开发者作公开的审校,您能够添加微信baobaotalk_com,验证输入“solidity”,而后将您的意见和建议发送给咱们,也能够在文末“留言”区留言,有效的建议咱们会采纳及合并进下一版本,同时将送一份小礼物给您以示感谢。git
贡献方式github
对于你们的帮助,咱们一如既往地感激。web
你能够试着 从源代码编译 开始,以熟悉 Solidity 的组件和编译流程。这对精通 Solidity 上智能合约的编写也有帮助。编程
咱们特别须要如下方面的帮助:数组
改善文档浏览器
回复 StackExchange(https://ethereum.stackexchange.com/) 和 Solidity Gitter(https://gitter.im/ethereum/solidity) 上的用户提问安全
解决并回复 Solidity's GitHub issues(https://github.com/ethereum/solidity/issues) 上的问题,特别是被标记为 up-for-grabs(https://github.com/ethereum/solidity/issues?q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs) 的问题,他们是针对外部贡献者的入门问题。微信
请用 GitHub issues tracker(https://github.com/ethereum/solidity/issues) 来报告问题。汇报问题时,请提供下列细节:app
你所使用的 Solidity 版本框架
源码(若是能够的话)
你在哪一个平台上运行代码
如何重现该问题
该问题的结果是什么
预期行为是什么样的
将形成问题的源码缩减到最少,老是颇有帮助的,而且有时候甚至能澄清误解。
为了进行贡献,请 fork 一个 develop 分支并在那里进行修改。除了你 作了什么 以外,你还须要在 commit 信息中说明,你 为何 作这些修改(除非只是个微小的改动)。
在进行了 fork 以后,若是你还须要从 develop 分支 pull 任何变动的话(例如,为了解决潜在的合并冲突),请避免使用 git merge ,而是 git rebase 你的分支。
此外,若是你在编写一个新功能,请确保你编写了合适的 Boost 测试案例,并将他们放在了 test/下。
可是,若是你在进行一个更大的变动,请先与 Solidity Development Gitter channel(https://gitter.im/ethereum/solidity-dev) 进行商量(与上文提到的那个功能不一样,这个变动侧重于编译器和编程语言开发,而不是编程语言的使用)。
新的特性和 bug 修复会被添加到 Changelog.md 文件中:使用的时候请遵循上述方式。
最后,请确保你遵照了这个项目的 编码风格(https://raw.githubusercontent.com/ethereum/solidity/develop/CODING_STYLE.md) 。还有,虽然咱们采用了持续集成测试,可是在提交 pull request 以前,请测试你的代码并确保它能在本地进行编译。
感谢你的帮助!
Solidity 有不一样类型的测试,他们包含在应用 soltest 中。其中一些须要 cpp-ethereum 客户端运行在测试模式下,另外一些须要安装 libz3。
soltest 会从保存在 ./test/libsolidity/syntaxTests 中的测试合约中获取所期待的结果。为了使 soltest 能够找到这些测试,可使用 --testpath 命令行参数来指定测试根目录,例如 ./build/test/soltest -- --testpath ./test。
若要禁用 z3 测试,可以使用 ./build/test/soltest -- --no-smt --testpath ./test ,若要执行不须要 cpp-ethereum 的测试子集,则用 ./build/test/soltest -- --no-ipc --testpath ./test。
对于其余测试,你都须要安装 cpp-ethereum ,并在测试模式下运行它:eth --test -d /tmp/testeth。
以后再执行实际的测试文件:./build/test/soltest -- --ipcpath /tmp/testeth/geth.ipc --testpath ./test。
能够用过滤器来执行一组测试子集:soltest -t TestSuite/TestName -- --ipcpath /tmp/testeth/geth.ipc --testpath ./test,其中 TestName 能够是通配符 *。
另外, scripts/test.sh 里有一个测试脚本可执行全部测试,并自动运行 cpp-ethereum,若是它在 scripts 路径中的话(但不会去下载它)。
Travis CI 甚至会执行一些额外的测试(包括 solc-js 和对第三方 Solidity 框架的测试),这些测试须要去编译 Emscripten 目标代码。
编写和运行语法测试
就像前文提到的,语法测试存储在单独的合约里。这些文件必须包含注解,为相关的测试标注预想的结果。测试工具将编译并基于给定的预想结果进行检查。
例如:./test/libsolidity/syntaxTests/double_stateVariable_declaration.sol
contract test { uint256 variable; uint128 variable; } // ---- // DeclarationError: Identifier already declared.
一个语法测试必须在合约代码以后包含跟在分隔符 ---- 以后的测试代码。上边例子中额外的注释则用来描述预想的编译错误或警告。若是合约不会出现编译错误或警告,这部分能够为空。
在上边的例子里,状态变量 variable 被声明了两次,这是不容许的。这会致使一个 DeclarationError 来告知标识符已经被声明过了。
用来进行那些测试的工具叫作 isoltest,能够在 ./test/tools/ 下找到。它是一个交互工具,容许你使用你喜欢的文本编辑器编辑失败的合约。让咱们把第二个 variable 的声明去掉来使测试失败:
contract test { uint256 variable; } // ---- // DeclarationError: Identifier already declared. 再次运行 ./test/isoltest 就会获得一个失败的测试: syntaxTests/double_stateVariable_declaration.sol: FAIL Contract: contract test { uint256 variable; } Expected result: DeclarationError: Identifier already declared. Obtained result: Success
这里,在得到告终果以后打印了预想的结果,但也提供了编辑/更新/跳过当前合约或直接退出的办法,isoltest 提供了下列测试失败选项:
edit:isoltest 会尝试打开先前用 isoltest --editor /path/to/editor 所指定的编辑器。若是没设定路径,则会产生一个运行时错误。若是指定了编辑器,这将打开编辑器并容许你修改合约代码。
update:更新测试中的合约。这将会移除包含了不匹配异常的注解,或者增长缺失的预想结果。而后测试会从新开始。
skip:跳过当前测试的执行。
quit:退出 isoltest。
在上边的状况自动更新合约会把它变为:
contract test { uint256 variable; } // ----
并从新运行测试。它将会经过:
Re-running test case... syntaxTests/double_stateVariable_declaration.sol: OK
Fuzzing 是一种测试技术,它能够经过运行多少不等的随机输入来找出异常的执行状态(片断故障、异常等等)。现代的 fuzzer 已经能够很聪明地在输入中进行直接的查询。 咱们有一个专门的程序叫作 solfuzzer,它能够将源代码做为输入,当发生一个内部编译错误、片断故障或者相似的错误时失败,但当代码包含错误的时候则不会失败。 经过这种方法,fuzzing 工具能够找到那些编译级别的内部错误。
咱们主要使用 AFL(http://lcamtuf.coredump.cx/afl/) 来进行 fuzzing 测试。你须要手工下载和构建 AFL。而后用 AFL 做为编译器来构建 Solidity(或直接构建 solfuzzer):
cd build # if needed make clean cmake .. -DCMAKE_C_COMPILER=path/to/afl-gcc -DCMAKE_CXX_COMPILER=path/to/afl-g++make solfuzzer
而后,你须要一个源文件例子。这将使 fuzzer 能够更容易地找到错误。你能够从语法测试目录下拷贝一些文件或者从文档中提取一些测试文件或其余测试:
mkdir /tmp/test_cases cd /tmp/test_cases # extract from tests:path/to/solidity/scripts/isolate_tests.py path/to/solidity/test/libsolidity/SolidityEndToEndTest.cpp # extract from documentation:path/to/solidity/scripts/isolate_tests.py path/to/solidity/docs docs
AFL 的文档指出,帐册(初始的输入文件)不该该太大。每一个文件自己不该该超过 1 kB,而且每一个功能最多只能有一个输入文件;因此最好从少许的输入文件开始。 此外还有一个叫作 afl-cmin 的工具,能够将输入文件整理为能够具备近似行为的二进制代码。
如今运行 fuzzer(-m 参数将使用的内存大小扩展为 60 MB):
afl-fuzz -m 60 -i /tmp/test_cases -o /tmp/fuzzer_reports -- /path/to/solfuzzer
fuzzer 会将致使失败的源文件建立在 /tmp/fuzzer_reports 中。一般它会找到产生类似错误的相似的源文件。 你可使用 scripts/uniqueErrors.sh 工具来过滤重复的错误。
Whiskers 是一个相似于 Mustache(https://mustache.github.io/) 的模板系统。编译器在各类各样的地方使用 Whiskers 来加强可读性,从而提升代码的可维护性和可验证性。
它的语法与 Mustache 有很大差异:模板标记 {{ 和 }} 被替换成了 < 和 > ,以便加强语法分析,避免与 内联汇编 的冲突(符号 < 和 > 在内联汇编中是无效的,而 { 和 } 则被用来限定块)。另外一个局限是,列表只会被解析一层,而不是递归解析。将来可能会改变这一个限制。
下面是一个粗略的说明:
任何出现 <name> 的地方都会被所提供的变量 name 的字符串值所替换,既不会进行任何转义也不会迭代替换。能够经过 <#name>...</name> 来限定一个区域。该区域中的内容将进行屡次拼接,每次拼接会使用相应变量集中的值替换区域中的 <inner> 项,模板系统中提供了多少组变量集,就会进行多少次拼接。顶层变量也能够在这样的区域的内部使用。
译者注:对于区域<#name>...</name>的释义,译者参考自:https://github.com/janl/mustache.js#sections
常见问题
这份清单最先是由 fivedogit 收集整理的。
能够在特定的区块上进行操做吗?(好比发布一个合约或执行一笔交易)
鉴于交易数据的写入是由矿工决定的而不是由提交者决定的,谁也没法保证交易必定会发生在下一个或将来某一个特定的区块上。这个结论适用于函数调用/交易以及合约的建立。
若是你但愿你的合约被定时调用,可使用:alarm clock(https://www.ethereum-alarm-clock.com/)。
什么是交易的“有效载荷(payload)”?
就是随交易一块儿发送的字节码“数据”。
存在反编译器吗?
除了 Porosity 有点接近以外,Solidity 没有严格意义上的反编译器。因为诸如变量名、注释、代码格式等会在编译过程当中丢失,因此彻底反编译回源代码是没有可能的。
不少区块链浏览器都能将字节码分解为一系列操做码。
若是区块链上的合约会被第三方使用,那么最好将源代码一块儿进行发布。
建立一个能够被停止并退款的合约
首先,须要提醒一下:停止合约听起来是一个好主意,把垃圾打扫干净是个好习惯,但如上所述,合约是不会被真正清理干净的。甚至,被发送至已移除合约的以太币,会今后丢失。
若是想让合约再也不可用,建议的作法是修改合约内部状态来使其 失效 ,让全部函数调用都变为无效返回。这样就没法使用这份合约了,并且发送过去的以太币也会被自动退回。
如今正式回答这个问题:在构造函数中,将 creator 赋值为 msg.sender ,并保存。而后调用 selfdestruct(creator); 来停止程序并进行退款。
例子(https://github.com/fivedogit/solidity-baby-steps/blob/master/contracts/05_greeter.sol)
须要注意的是,若是你已经在合约顶部作了引用 import "mortal" 而且声明了 contract SomeContract is mortal { ... ,而后再在已存在此合约的编译器中进行编译(包含 Remix),那么 kill() 就会自动执行。当一份合约被声明为 mortal 时,你能够仿照个人例子,使用 contractname.kill.sendTransaction({from:eth.coinbase}) 来停止它。
调用 Solidity 方法能够返回一个数组或字符串(string)吗?
能够。参考 array_receiver_and_returner.sol 。
可是,在 Solidity内部 调用一个函数并返回变长数据(例如 uint[] 这种变长数组)时,每每会出现问题。这是 以太坊虚拟机Ethereum Virtual Machine(EVM) 自身的限制,咱们已经计划在下一次协议升级时解决这个问题。
将变长数据做为外部交易或调用的一部分返回是没问题的。
数组可使用 in-line 的方式(指在声明变量的同一个语句中)来初始化吗?好比: string[] myarray = ["a", "b"];
能够。然而须要注意的是,这方法如今只能用于定长 内存memory 数组。你甚至能够在返回语句中用 in-line 的方式新建一个 内存memory 数组。听起来很酷,对吧!
例子:
pragma solidity ^0.4.16; contract C { function f() public pure returns (uint8[5]) { string[4] memory adaArr = ["This", "is", "an", "array"]; return ([1, 2, 3, 4, 5]); } }
合约的函数能够返回结构(struct)吗?
能够,但只适用于内部(internal)函数调用。
我从一个返回的枚举类型(enum)中,使用 web3.js 只获得了整数值。我该如何获取具名数值?
虽然 Solidity 支持枚举类型,但 ABI(应用程序二进制接口)并不支持。当前阶段你须要本身去作映射,未来咱们可能会提供一些帮助。
可使用 in-line 的方式来初始化状态变量吗?
能够,全部类型均可以(甚至包括结构)。然而须要注意的是,在数组使用这个方法的时候须要将其定义为静态 内存memory 数组。
例子:
pragma solidity ^0.4.0; contract C { struct S { uint a; uint b; } S public x = S(1, 2); string name = "Ada"; string[4] adaArr = ["This", "is", "an", "array"]; } contract D { C c = new C(); }
结构(structs)如何使用?
参考 struct_and_for_loop_tester.sol 。
循环(for loops)如何使用?
和 JavaScript 很是相像。但有一点须要注意:
若是你使用 for (var i = 0; i < a.length; i ++) { a[i] = i; } ,那么 i 的数据类型将会是 uint8,须要从 0 开始计数。也就是说,若是 a 有超过 255 个元素,那么循环就没法停止,由于 i 最大只能变为 255。
最好使用 for (uint i = 0; i < a.length...
参考 struct_and_for_loop_tester.sol。
有没有一些简单的操做字符串的例子(substring,indexOf,charAt 等)?
这里有一些字符串相关的功能性函数 stringUtils.sol ,而且会在未来做扩展。另外,Arachnid 有写过 solidity-stringutils。
当前,若是你想修改一个字符串(甚至你只是想获取其长度),首先都必须将其转化为一个 bytes
pragma solidity ^0.4.0; contract C { string s; function append(byte c) public { bytes(s).push(c); } function set(uint i, byte c) public { bytes(s)[i] = c; } }
我能拼接两个字符串吗?
目前只能经过手工实现。
为何你们都选择将合约实例化成一个变量(ContractB b;),而后去执行变量的函数(b.doSomething();),而不是直接调用这个 低级函数low-level function .call() ?
若是你调用实际的成员函数,编译器会提示诸如参数类型不匹配的问题,若是函数不存在或者不可见,他也会自动帮你打包参数。
参考 ping.sol 和 pong.sol 。
没被使用的 gas 会被自动退回吗?
是的,立刻会退回。也就是说,做为交易的一部分,在交易完成的同时完成退款。
当返回一个值的时候,好比说 uint 类型的值, 能够返回一个 undefined 或者相似 null 的值吗?
这不可能,由于全部的数据类型已经覆盖了所有的取值范围。
替代方案是能够在错误时抛出(throw),这一样能复原整个交易,当你遇到意外状况时不失为一个好的选择。
若是你不想抛出,也能够返回一对(a pair)值
pragma solidity >0.4.23 <0.5.0; contract C { uint[] counters; function getCounter(uint index) public view returns (uint counter, bool error) { if (index >= counters.length) return (0, true); else return (counters[index], false); } function checkCounter(uint index) public view { (uint counter, bool error) = getCounter(index); if (error) { // ... } else { // ... } } }
注释会被包含在已部署的合约里吗,并且会增长部署的 gas 吗?
不会,全部执行时非必须的内容都会在编译的时候被移除。 其中就包括注释、变量名和类型名。
若是在调用合约的函数时一块儿发送了以太币,将会发生什么?
就像在建立合约时发送以太币同样,会累加到合约的余额总数上。 你只能够将以太币一块儿发送至拥有 payable 修饰符的函数,否则会抛出异常。
合约对合约的交易能够得到交易回执吗?
不能,合约对合约的函数调用并不会建立前者本身的交易,你必需要去查看所有的交易。这也是为何不少区块浏览器没法正确显示合约对合约发送的以太币。
关键字 memory 是什么?是用来作什么的?
以太坊虚拟机Ethereum Virtual Machine(EVM) 拥有三类存储区域。
第一类是 存储storage,贮存了合约声明中全部的变量。 虚拟机会为每份合约分别划出一片独立的 存储storage 区域,并在函数相互调用时持久存在,因此其使用开销很是大。
第二类是 内存memory,用于暂存数据。其中存储的内容会在函数被调用(包括外部函数)时擦除,因此其使用开销相对较小。
第三类是栈,用于存放小型的局部变量。使用几乎是免费的,但容量有限。
对绝大部分数据类型来讲,因为每次被使用时都会被复制,因此你没法指定将其存储在哪里。
在数据类型中,对所谓存储地点比较重视的是结构和数组。 若是你在函数调用中传递了这类变量,假设它们的数据能够被贮存在 存储storage 或 内存memory 中,那么它们将不会被复制。也就是说,当你在被调用函数中修改了它们的内容,这些修改对调用者也是可见的。
不一样数据类型的变量会有各自默认的存储地点:
状态变量老是会贮存在 存储storage中
函数参数默认存放在内存memory中
结构、数组或映射类型的局部变量,默认会放在存储storage中
除结构、数组及映射类型以外的局部变量,会储存在栈中
例子:
pragma solidity ^0.4.0; contract C { uint[] data1; uint[] data2; function appendOne() public { append(data1); } function appendTwo() public { append(data2); } function append(uint[] storage d) internal { d.push(1); } }
函数 append 能一块儿做用于 data1 和 data2,而且修改是永久保存的。若是你移除了 storage 关键字,函数的参数会默认存储于 memory。这带来的影响是,在 append(data1) 或 append(data2)被调用的时候,一份全新的状态变量的拷贝会在 内存memory 中被建立,append 操做的会是这份拷贝(也不支持 .push ——但这又是另外一个话题了)。针对这份全新的拷贝的修改,不会反过来影响 data1 或 data2。
一个常见误区就是声明了一个局部变量,就认为它会建立在 内存memory 中,其实它会被建立在 存储storage 中:
/// 这份合约包含一处错误 pragma solidity ^0.4.0; contract C { uint someVariable; uint[] data; function f() public { uint[] x; x.push(2); data = x; } }
局部变量 x 的数据类型是 uint[] storage,但因为 存储storage 不是动态分配的,它须要在使用前经过状态变量赋值。因此 x 自己不会被分配 存储storage 的空间,取而代之的是,它只是做为 存储storage 中已有变量的别名。
实际上会发生的是,编译器将 x 解析为一个 存储storage 指针,并默认将指针指向 存储插槽storage slot 0 。这就形成 someVariable (贮存在 存储插槽storage slot 0)会被 x.push(2) 更改。(在本例中,两个合约变量 someVariable 和 data 会被预先分配到两个 存储插槽storage slot 中,即 存储插槽storage slot 0 和 存储插槽storage slot 1 。上面的程序会使局部变量 x 变成指向保存了变量 someVariable 的 存储插槽storage slot 0 的指针。译者注。)
正确的方法以下:
pragma solidity ^0.4.0; contract C { uint someVariable; uint[] data; function f() public { uint[] x = data; x.push(2); } }
怎样才能在合约中获取一个随机数?(实施一份自动回款的博彩合约)
作好随机这件事情,每每是一个加密项目最关键的部分,大部分的失败都来自于使用了低劣的随机数发生器。
若是你不考虑安全性,能够作一个相似于 coin flipper 的东西,反之,最好调用一份能够提供随机性的合约,好比 RANDAO 。
从另外一份合约中的 non-constant 函数获取返回值
关键点是调用者(合约)须要了解将被调用的函数。
参考 ping.sol 和 pong.sol 。
让合约在首次被挖出时就开始作些事情
使用构造函数。在构造函数中写的任何内容都会在首次被挖出时执行。
参考 replicator.sol 。
怎样才能建立二维数组?
参考 2D_array.sol 。
须要注意的是,用 uint8 类型的数据填满一个 10x10 的方阵,再加上合约建立,总共须要花费超过 800,000 的 gas。若是是 17x17 须要 2,000,000 的 gas。然而交易的 gas 上限是 314 万。。。好吧,其实你也玩不了太大的花样。
注意,“建立”数组纯粹是免费的,成本在于填充数组。
还需注意,优化 存储storage 访问能够大大下降 gas 的花费,由于一个 存储插槽storage slot 能够存放下 32 个 uint8类型的值。但这类优化目前也存在一些问题:在跨循环的时候不起做用;以及在边界检查时候会出问题。固然,在将来这种状况会获得改观。
当咱们复制一个结构(struct)时, 结构 (struct)中定义的映射会被怎么处理?
这是一个很是有意思的问题。假设咱们有一份合约,里面的字段设置以下:
struct User { mapping(string => string) comments; } function somefunction public { User user1; user1.comments["Hello"] = "World"; User user2 = user1; }
在这种状况下,因为缺失“被映射的键列表”,被复制至 userList 的结构中的映射会被忽视。所以,系统没法找出什么值能够被复制过去。
我应该如何初始化一份只包含指定数量 wei 的合约?
目前实现方式不是太优雅,固然暂时也没有更好的方法。 就拿 合约A 调用一个 合约B 的新实例来讲,new B 周围必需要加括号,否则 B.value 会被认做是 B 的一个成员函数,叫作 value。 你必须确保两份合约都知道对方的存在,而且 合约B 拥有 payable 构造函数。
就是这个例子:
pragma solidity ^0.4.0; contract B { function B() public payable {} } contract A { address child; function test() public { child = (new B).value(10)(); //construct a new B with 10 wei } }
合约的函数能够接收二维数组吗?
二维数组还没法使用于外部调用和动态数组——你只能使用一维的动态数组。
bytes32 和 string 有什么关系吗?为何 bytes32 somevar = "stringliteral"; 能够生效,还有保存下来的那个 32 字节的 16 进制数值有什么含义吗?
数据类型 bytes32 能够存放 32 个(原始)字节。在给变量分配值的过程当中 bytes32 samevar = "stringliteral";, 字符串已经被逐字翻译成了原始字节。若是你去检查 somevar ,会发现一个 32 字节的 16 进制数值,这就是用 16 进制表示的 字符串的文字 。
数据类型 bytes 与此相似,只是它的长度能够改变。
最终来看,假设 bytes 储存的是字符串的 UTF-8 编码,那么它和 string 基本是等同的。因为 string 存储storage 的是 UTF-8 编码格式的数据,因此计算字符串中字符数量的成本是很高的(某些字符的编码甚至大于一个字节)。所以,系统还不支持 string s; s.length ,甚至不能经过索引访问 s[2] 。但若是你想访问字符串的下级字节编码,可使用 bytes(s).length 和 bytes(s)[2],它们分别会返回字符串在 UTF-8 编码下的字节数量(不是字符数量)以及字符串 UTF-8 编码的第二个字节(不是字符)。
一份合约能够传递一个数组(固定长度)或者一个字符串或者一个 bytes (不定长度)给另外一份合约吗?
固然能够。但若是不当心跨越了 内存memory / 存储storage 的边界,一份独立的拷贝就会被建立出来:
pragma solidity ^0.4.16; contract C { uint[20] x; function f() public { g(x); h(x); } function g(uint[20] y) internal pure { y[2] = 3; } function h(uint[20] storage y) internal { y[3] = 4; } }
因为会在 内存memory 中对 存储storage 的值建立一份独立的拷贝(默认存储在 内存memory 中),因此对 g(x) 的调用其实并不会对 x 产生影响。另外一方面,因为传递的只是引用而不是一个拷贝, h(x) 得以成功地修改了 x。
有些时候,当我想用相似这样的表达式: arrayname.length = 7; 来修改数组长度,却会获得一个编译错误 Value must be an lvalue。这是为何?
你可使用 arrayname.length = <some new length>; 来调整 存储storage 中的动态数组(也就是在合约级别声明的数组)的长度。若是你获得一个 lvalue 错误,那么你有可能作错了如下两件事中的一件或所有。
你在尝试修改长度的数组多是保存在 内存memory中的,或者
你可能在尝试修改一个非动态数组的长度。
// 这将没法编译经过 pragma solidity ^0.4.18; contract C { int8[] dynamicStorageArray; int8[5] fixedStorageArray; function f() { int8[] memory memArr; // 第一种状况 memArr.length++; // 非法 int8[5] storage storageArr = fixedStorageArray; // 第二种状况 storageArr.length++; // 非法 int8[] storage storageArr2 = dynamicStorageArray; storageArr2.length++; // 非法 } }
**重要提醒: **在 Solidity 中,数组维数的声明方向是和在 C 或 Java 中的声明方向相反的,但访问方式相同。
举个例子,int8[][5] somearray; 是5个 int8 格式的动态数组。
这么作的缘由是,T[5] 老是能被识别为5个 T 的数组,哪怕 T 自己就是一个数组(而在 C 或 Java 是不同的)。
Solidity 的函数能够返回一个字符串数组吗(string[])?
暂时还不能够,由于这要求两个维度都是动态数组(string 自己就是一种动态数组)。
若是你发起了一次获取数组的调用,有可能得到整个数组吗?仍是说另外须要写一个辅助函数来实现?
一个数组类型的公共状态变量会有一个自动的获取函数 getter function , 这个函数只会返回单个元素。若是你想获取完整的数组,那么只能再手工写一个函数来实现。
若是某个帐户只存储了值但没有任何代码,将会发生什么?例子: http://test.ether.camp/account/5f740b3a43fbb99724ce93a879805f4dc89178b5
构造函数作的最后一件事情是返回合约的代码。这件事消耗的 gas 取决于代码的长度,其中有种可能的状况是提供的 gas 不够。这是惟一的一种状况下,出现了 “out of gas” 异常却不会去复原改变了的状态,这个改变在这里就是对状态变量的初始化。
https://github.com/ethereum/wiki/wiki/Subtleties
当 CREATE 操做的某个阶段被成功执行,若是这个操做返回 x,那么 5 * len(x) 的 gas 在合约被建立前会从剩余 gas 中被扣除。若是剩余的 gas 少于 5 * len(x),那么就不进行 gas 扣除,而是把建立的合约代码改变成空字符串,但这时候并不认为是发生了异常——不会发生复原。
在定制 通证token 的合约中,下面这些奇怪的校验是作什么的?
require((balanceOf[_to] + _value) >= balanceOf[_to]);
在Solidity(以及大多数其余机器相关的编程语言)中的整型都会被限定在必定范围内。 好比 uint256 ,就是从 0 到 2**256 - 1 。若是针对这些数字进行操做的结果不在这个范围内,那么就会被截断。这些截断会带来 严重的后果 ,因此像上面这样的代码须要考虑避免此类攻击。
更多问题?
若是你有其余问题,或者你的问题在这里找不到答案,请在此联系咱们 gitter(https://gitter.im/ethereum/solidity) 或者提交一个 issue(https://github.com/ethereum/solidity/issues)。
延伸阅读:智能合约-Solidity官方文档(1)
根据例子学习Solidity-Solidity官方文档(3)
深刻理解Solidity之源文件及合约结构——Solidity中文文档(4)
应用二进制接口(ABI) 说明——Solidity中文文档(7)
点击“阅读原文”便可查看完整中文文档
注:本文为solidity翻译的第十三部分《贡献方式及常见问题》,特发布出来邀请solidity爱好者、开发者作公开的审校,您能够添加微信baobaotalk_com,验证输入“solidity”,而后将您的意见和建议发送给咱们,也可在文末“留言”区留言,或经过原文连接访问咱们的Github。有效的建议咱们会收纳并及时改进,同时将送一份小礼物给您以示感谢。
本文内容来源于HiBlock区块链社区翻译小组,感谢全体译者的辛苦工做。点击“阅读原文”便可查看完整中文文档。
线上课程推荐 --
线上课程:《8小时区块链智能合约开发实践》
培训讲师:《白话区块链》做者 蒋勇
课程原价:999元,现价 399元
更多福利:
点击“阅读原文”便可查看完整中文文档