《智能合约开发课》学习8:Solidity的语法知识点
最近报名参加了硅谷区块链举办的《智能合约开发课》第二期培训班,根据培训要求,不能透露课程的内容,但我会在steemit上记录我的成长过程。
- 第0篇笔记:预习
- 第1篇笔记:两个简单的合约
- 第2篇笔记:小试Truffle
- 第3篇笔记:合约解读
- 第4篇笔记:Mocha测试框架
- 第5篇笔记:Hello World
- 第6篇笔记:收币代投
- 第7篇笔记:发给多个参投方
单纯地学Solidity的语法知识点非常枯燥,而放在一个个的实际例子中逐步深入时,则轻松了许多,当前已经学完了智能合约开发课的第三课,必须把学过的语法点抓紧总结一下。
一、文件名
solidity文件的扩展名为*.sol
二、指定编译器版本
pragma solidity ^0.4.0;
表示源程序在大于等于0.4.0版本的编译器可以正常工作,在大于等于0.5.0版本中的编译器中无法工作。即:
0.4.0 <= version < 0.5.0
关于pragma的详细文档在这里:http://solidity.readthedocs.io/en/develop/layout-of-source-files.html#version-pragma
而版本号之前的“^”符号的含义,来自于npm中的语法 。
三、数据类型
solidity是静态类型语言,所有变量需要有定义声明。
1)整数
常用的无符号整数类型有uint8, uint16, uint24, ... ,uint256。256个字节的无符号整数uint256可以简写为uint。而有符号的整数则从int8, int16一直到int256。
这些整数经常会用来保存用户的token数量,小心加、减、乘、除运算后的结果溢出,那可是非常惨痛的损失。
uint a = 365;
2)地址
address用来存储以太坊的地址,实际上就是不超过20字节的无符号整数,例如:
address a = 0xdd870fa1b7c4700f2bd7f44238821c26f7392148;
后面的0x开头的一串十六进制数并不是字符串,不需要双引号。而在remix调试程序时,传入的地址参数却需要双引号括起来,新手一开始经常会遇到这个错误。
地址有合约地址和普通的钱包地址两种。
3)结构struct
与C语言非常相似,不用多说。
struct Participator {
address addr;
uint amount;
}
4)数组
Solidity支持定长数组:
uint[5] a;
也支持动态数组:
uint []b;
在动态数组中增加一个元素用push()函数。
b.push(1);
b.push(2);
用b.length
得到数组的长度,还可以直接修改length来删除元素。
b.length = 1;
5)mapping类型
这种类型相当于其它语言中的哈希表,一开始不太适应,是solidity中非常重要的一种数据类型,以后再展开。
6)var
var并不是表示动态类型,而是让书写更简单,一个值在分配给var变量时,其类型就已经确定了。如果要赋值给其它类型,仍要进行强制类型转换。
7)其它类型
solidity中还支持布尔类型、字符串类型、枚举类型等等。非常神奇,这次的培训课中竟然一直没讲string类型,通常的编程语言都会在第一课介绍"hello world"。可能智能合约是与token打交道,而不是输出字符串吧。
四、函数修饰符
function modifier可以让函数显得更加简洁,比如经常判断一个函数的msg.sender是不是合约构建者时,不需要频繁插入require(msg.sender == owner);
这样的语句,只需要定义一个modifier。
modifier onlyOwner {
require(msg.sender == owner);
_;
}
而在函数声明的主体尾部加上onlyOwner就可以了。上面的语法中最有意思的是_;
这行语句,表示原来函数中的所有语句。当函数中含有return()语句时,替代规则有点特殊。
function test() onlyOwner {
// ...
}
payable实际是一个内置的修饰符,表示一个函数在调用时要发送ether。
五、继承
solidity支持多重继承,继承线采用与Python相似的C3 Linearization规则。
contract parent {
// ...
}
contract child is parent {
// ...
}
抽象合约是函数只有声明,没有具体的实现。
interface与java语言的语法类似,不用多说。
六、多参数返回
在函数的返回值中可以一次返回多个参数,比如:在返回一个数组中的元素的同时,返回它在数组中所在的位置时,这样可以一次给多个变量赋值。
function test() returns (address item, uint index) {
address []arr;
// ...
return (arr[1], 1);
}
address owner;
uint i;
(owner, i) = test();
(owner, ) = test(); //如果不需要第2个参数
七、异常处理
在比较早的solidity版本中都用throw(),现在统统用revert(),可以保证在遇到异常时,回滚到调用前的状态。用require可以写得更简练。
require(msg.sender == owner);
另外的一个容易让人搞糊涂的语句是assert,在C语言中,assert翻译为“断言”,这类语句只在调试时起作用,用来排查软件的重大BUG,这里也是类似。表示程序在执行到这条语句时,肯定会满足其中的情况。如果有异常发生,说明软件肯定有重大的BUG,由于solidity中涉及到转帐等重要操作,assert失败后,会耗光所有的GAS,让交易失败,防止更严重的事情发生。assert常用于数组越界、元素非空的检查上。
而require要检查的是软件可能经常发生的情况,比如给函数中传递的参数时是否满足一定的条件等等。
八、几个全局变量
solidity中内置了msg,block和tx这几个全局变量。
- msg.value,消息所附带的货币量,单位为wei
- msg.sig,调用数据的前四个字节,函数标识符
- msg.sender,当前调用发起人的地址
- msg.gas,当前剩余的gas
- block.difficulty,当前区块的难度值
- block.blockhash(),某个区块的哈希值
- block.coinbase,当前区块矿工的地址
- block.gaslimit,当前区块的gas上限
- block.number,当前区块的序号
- block.timestamp,当前区块的时间戳,是uint类型
- now,等同于block.timestamp
- tx.gasprice,交易的gas价格
- tx.origin,交易的发起人,完整的调用链
九、可见性
函数的可见性有external、public、internal和private。
状态变量的可见性有public、internal和private,类似于C++语言中的public、protected和private。
external只能修饰函数,说明这个函数只能被外部合约调用。假设函数f()是external,还想在合约内调用,可以用this.f()。
十、delete
delete操作可以用于任何变量,将其设置为默认值0。
对可变数组使用delete,会删除所有元素,其长度变为0。
对定长数组使用delete,则会重置所有元素为0,也可以重置指定位置的元素。
对map类型使用delete,什么也不会发生。
对map类型的一个键使用delete,则会删除与该键相关的值。
本文由币乎(bihu.com)内容支持计划奖励
我们又见面喽!请接受cn区点赞机器人 @cnbuddy 对你作为cn区一员的感谢。让我们携手努力,共同促进cn区快速发展。更多cn区动态,请留意我的主页。如果不想再收到我的留言,请回复“取消”。
榜样。来到世上就是为了学习来了。
赞!