《智能合约开发课》学习8:Solidity的语法知识点

in #cn7 years ago (edited)

img最近报名参加了硅谷区块链举办的《智能合约开发课》第二期培训班,根据培训要求,不能透露课程的内容,但我会在steemit上记录我的成长过程。

单纯地学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)内容支持计划奖励

Sort:  

我们又见面喽!请接受cn区点赞机器人 @cnbuddy 对你作为cn区一员的感谢。让我们携手努力,共同促进cn区快速发展。更多cn区动态,请留意我的主页。如果不想再收到我的留言,请回复“取消”。

榜样。来到世上就是为了学习来了。