Solidity基础入门讲解

  • Post author:
  • Post category:solidity


Solidity的语言类型:

  1. 静态类型的语言:编译前变量类型需要先确定
  2. 变量可以分为:

    值类型:赋值或者传参时总是进行值拷贝

    引用类型:传递的是地址

    这里先给出一个总览图:

    这里写图片描述

整型:

和其他语言类型,可以用int关键字声明:

有符号整数:int8,int16,int32,……int255,

无符号整数:uint8,uint16,uint32,……uint255,

跟在关键字后的数字表示多少字节,表示数大小的范围,跟默认int,表示255。


注意



整型的溢出问题分为:向上溢出和向下溢出,例如:

uint8表示的最大数==》255,如果此时加1,会出现向上溢出的错误,此时的结果为:0;uint8表示的最小值==》0,此时如果减1,会出现向下溢出的错误,结果为:255。

如何解决上述的溢出问题呢?

可以使用assert(断言)关键字,进行预判断处理。

定点浮点型:

关键字:

fixed



ufixed

,对应于其他语言的float和double.

3. 使用时需要声明位数,指明大小

4. 格式:

fixedMN,其中M为:数值,代表所占的空间位数,N为:小数点位数,M以8步进,可为8到256位,N可为0到80之间。

定长字节数组:

关键字:bytes

占空间固定的数组:

bytes1,bytes2,…bytes32,其中的数字的单位为字节。

1.通过属性.length获取字节数组的长度

2.可以像字符串一样使用:bytes2=”couse”

3.可以想整型一样运算比较和位运算

4.像数组一样使用索引:bytes8[k],返回第k+1个字节,0<=k<8

常量:

整型常量:1,23,1.55

字符串常量:不支持“+”运算

十六进制常量:hex”aabb”

枚举:

1.关键字enum用来自定义类型:如下

enum ColorChioce{Red,Bule,Yellow}

2.可与整数进行转换,但不能进行隐式转换

3.枚举类型应至少有一名成员

地址类型:

关键字:address,表示一个账户地址(20字节)

属性:.balance,获取余额,单位是wei,1eth(以太币)=10^18wei

成员函数:

1.transfer(),转账函数

2.send(),也是转账函数,错误时不发生异常返回false,使用时一定要检查返回值,大部分时候使用transfer()。

addr.transfer(y) 等价于 require(addr.send(y))


注意:


send()和transfer()使用时有2300gas限制,当合约接收以太币时,转账容易失败。

3.call(),可以调用另外一个合约地址的函数,如下:

addr.call(函数签名,参数):调用对应函数

addr.call.value(y)()功能上类似addr.transfer(y),但没有gas的限制。

addr.call.gas():指定gas,指定调用函数消耗的gas。

补充:如何区分合约地址及外部地址?

答:evm提供了一个操作码EXTCODESIZE,用来获取地址相关联的代码大小(长度),如果是外部帐号地址,没有代码返回0,因此我们可以使用以下方法判断合约地址及外部帐号地址:

uint256 size;
assembly { size := extcodesize(addr) }
return size > 0;
}

函数类型

solidity中,函数是一种类型。像其他类型⼀一样,函数类型可以作为变量量类型,也可以作为返回值类型(函数式语⾔言的特点)


函数分为两种类型:


1.外部(external)函数:发起EVM消息调用,使用:( 地址.函数名) 进行调用

2.内部(internal)函数:代码跳转调用, gas小的多,但法在合约外部调用,使用: (函数名) 进行调用,默认是内部(internal)函数。


声明格式:


1.声明一个函数:function foo(int) 或者 function foo(int) external returns(int)

2.声明一个韩式类型变量:function(int) foo 或者 function(int) external returns(int) foo

数据储存位置:

1.storage(区块链中):永久储存在区块链链中。

例:①状态变量

contract A {
uint a;
}

②复杂类型的局部变量

contract A {
function fun() {
uint[] arr;
}
}

2.memory(EVM内存中):随着交易的结束,内存释放。

例:局部变量

contract A {
function fun(uint[] a) {
uint b;
}}

两者的赋值表现:

在memory和storage之间总是会创建一个完全独⽴立的拷贝。

状态变量量之间相互赋值,总是会创建一个完全独⽴立的拷贝。

同样的数据位置之间赋值只是引⽤用的传递。

示例代码:

contract TestLoc(){
    uint[] x;//x的储存位置是storage

    function f(uint[] memoryArray) public returns (uint[]){

    x=memoryArray;//从memory 复制到storage

    uint[] y=x;//storage引用传递给复杂局部变量y(y是一个storage引用)
    y[1]=2;     //此时x,y都会被修改

    g(x);//引用传递,g可以改变x的内容
    h(x);//拷贝到memory,h无法改变x的内容
}
    function g(uint[] storage storageArray) internal{}
    function h(uint[] memoryArray) public {}
}

数组:


数组声明的两种格式:



1.普通格式

:T[k]

T: 元素类型, k: 数组长度(可选)

uint [10] tens; uint [ ] us;

uint [] public u = [1, 2, 3]; //public数组状态变量量会自动生成一个对应变量量的函数,数组长度省略


2.使用new 关键字:


uint[] c = new uint


;、需要指定长度,不能省略


属性:



1.length,获取长度


uint [10] tens; //tens.length 为10

storage的变长数组,可以通过给.length赋值调整数组长度

memory数组一旦创建,大小不不可调整


2..push() 添加元素,返回新长度


仅针对变长数组,memory数组不支持

字节数组:


字节数组bytes:


类似 byte[ ] , 不过bytes作为外部函数的参数时占用的空间更更小,推荐使用,相比前面的定长字节数组,这个是不定长的。


声明形式:

bytes mybs;

bytes mybs=“hello world”

字符串:

solidity中,字符串也是数组


声明:

string mystr;

string mystr=”Solidity入门详解”;


注意:


字符串没有.length属性 ,但是可以通过bytes(s).length 获取字节数组的长度,但也只是字节数组的长度,没法获得字符串长度。

另外还可通过bytes(s)[k] : 获取字节数组下标k的UTF-8 编码。


bytes和string都可以储存字符串,那他们的区别是声明呢?


两者储存数据的方式不一样

bytes: 用来存储任意长度的字节数据

string: 用来存储UTF-8编码的字符串数据


前面说到我们没法获取字符串的长度,那怎么才能获取呢?


引入第三方库:stringutils,github地址:

https://github.com/Arachnid/solidity-stringutils


该第三库支持很多强大的功能:

获取字符串长度

匹配字符串串:find(), startsWith(),endsWith()

字符串串拼接

….

映射


关键字

:mapping


声明一个映射类型:


mapping(键=> 值) public mymapping

键类型不能是变长数组、合约类型、嵌套类型值类型无限制

如下:

mapping(address => uint) public balances;


访问形式:


索引访问,mymapping[键]


限制:


1.只能作为状态变量,不能放在函数里声明

2.无法遍历访问,没有长度,没有键集合,没有值集合


解决限制


因为solidity提供的这个mapping的功能实在有限,因此我们可以借助第三方库来实现一些有用的功能,第三方库github地址为:

https://github.com/ethereum/dapp-bin/blob/master/library/iterable_mapping.sol

结构体


关键字:

struct,类似C语言的结构体


定义一个结构体:

struct First{
bool myBool;
uint myInt;
}

其中结构体的类型可以为:基本类型、数组、结构体、映射


声明与初始化:


仅声明变量,不初始化:

First  f1;

按成员顺序初始化:

First  f1 = First(true,1);

命名方式初始化:

First  f1 = First({myBool:true,myInt:1});


访问与赋值:

First  f1 ;
f1.myBool=true;


使用限制:


结构体目前仅支持在合约内部使用,或继承合约内使用,如果要在参数和返回值中使用结构体,函数必须声明internal。

类型转换:

将一个类型转为另一个类型,转换可分为隐式转换和显式转换。


1.隐式转换:


运算符两边有不同的类型,不会丢失数据下,编译器会尝试隐式转换类型,如下:

uint8 -> uint16,uint256

uint16,uint256 -> uint8(出错)


2.显式转换:


如果编译器不允许隐式的自动转换,但你知道转换没有问题时,可以进行强转。


注意:

不正确的转换会带来错误,如果转换为一个更小的类型,高位将被截断。

重置变量

对一个变量进行重置


关键字

:delete

**使用:**delete 变量名;

使用重置变量后,变量会变为默认值,下面列出各个类型的默认值:

bool -> false;
uint -> 0
address -> 0x0
bytes -> 0x0
string ->""
注意:
delete 对映射无效

例如:

aa=true;
delete aa;//aa变为false

uint[] memory arr = new uint[](7);
delete arr; // a.length = 0;

CustomType memory ct = CustomType(true, 100);
delete ct; // ct.myBool = false ; myInt = 0;


特别注意:


delete 不影响值拷贝的变量,但是如果是传递引用的变量,就会受到影响。

内置API


使用时间日期:


1.now函数:获取当前时间

2.引入第三方库执行更多功能,推荐github·上一个第三方库:

https://github.com/pipermerriam/ethereum-datetime



区块和交易相关API:

blockhash(uint blockNumber) returns (bytes32):返回给定区块号的哈希值,只支持最近256个区块,且不包含当前区块。
block.coinbase (address): 当前块矿工的地址。
block.difficulty (uint):当前块的难度。
block.gaslimit (uint):当前块的gaslimit。
block.number (uint):当前区块的块号。
block.timestamp (uint): 当前块的Unix时间戳(从1970/1/1 00:00:00 UTC开始所经过的秒数)
gasleft() (uint256): 获取剩余gas。
msg.data (bytes): 完整的调用数据(calldata)。
msg.gas (uint): 当前还剩的gas(弃用)。
msg.sender (address): 当前调用发起人的地址。
msg.sig (bytes4):调用数据(calldata)的前四个字节(例如为:函数标识符)。
msg.value (uint): 这个消息所附带的以太币,单位为wei。
now (uint): 当前块的时间戳(block.timestamp的别名)
tx.gasprice (uint) : 交易的gas价格。
tx.origin (address): 交易的发送者(全调用链)


什么是ABI?


ABI是Application Binary Interface的缩写,即应用程序二进制接口。

当我们向 合约地址发送一个交易,交易的内容就ABI编码数据。

作用:用来计算一个函数以及参数的ABI编码数据。

ABI的编码函数有如下这些,用来直接得到ABI编码信息:

* abi.encode(...) returns (bytes):计算参数的ABI编码。
* abi.encodePacked(...) returns (bytes):计算参数的紧密打包编码
* abi. encodeWithSelector(bytes4 selector, ...) returns (bytes): 计算函数选择器和参数的ABI编码
* abi.encodeWithSignature(string signature, ...) returns (bytes): 等价于* abi.encodeWithSelector(bytes4(keccak256(signature), ...)


错误处理:


solidity的错误处理如下:

当发生错误处理的时候,会出现事件的回滚。

do something1

do error

do something2

当执行到do error,错误发生,此时 do something2,do something1都不会执行。

错误处理函数:

1.assert(bool condition):用于判断内部错误,天剑不满足时抛出异常,此时会消耗掉所剩余的gas。

2.require(bool condition):用于判断输或者外部组件错误,条件不满足时判处异常,此时gas会返回还给调用者,区别于assert。

require(bool condition,string message):同时上,多了一个错误信息。

3.revert():终止执行还原改变的状态。

revert(string,reason):同上,提供一个错误提醒信息。


用require还是assert?


使用require的情况:

1.用于检查用户输入;

2.用于检查合约调用返回值,如require(addr.send(1));

3.用于检查状态,如:msg.sender==owner;

4.通常用于函数的开头;

5.不知道使用哪一个的时候

使用assert的情况:

1.用于检查溢出错误,如‘z=x+y;assert(z>=x)’;

2.用于检查不应该发生的异常情况;

3.用于在状态改变之后,检查合约状态;

4.尽量少用assert;

5.通常用于函数中间或者结尾;

函数修改器:

关键字:modifier

函数修改器可以改变一个函数的行为,通常用于在函数执行检查某种前置条件。

modifier onlyOwener{
        require(msg.sender==owner);
        _;
}

function my() public onlyOwener{
        //do something;
}

函数修改器修饰函数时,函数题被插入到“_”处。所以在执行my()函数前,会执行onlyOwener这个函数,如果检查通过,再执行my()函数。


一些补充:


1.函数修改器可被继承

2.函数修改器可接受参数

3.多个函数修改器一起使用(空格隔开,修改器会一次检查执行)

函数修饰符:

1.payable:边表示一个函数能附加以太币调用:

function f(uint a) public payable returns (uint):{

}

2.view:表示一个函数不能修改状态。

view函数和constant等价,constant在0.5版本之后会弃用。使用了这个修饰符的函数不会消耗gas。

3.pure:表示一个函数不读取状态,也不修改状态。本地执行不消耗gas。

solidity的继承:

1.继承的合约可以访问所有非private成员。

external:外部访问、

public:内外都可以、

internal:内部级继承、

private:内部,继承的也不行

2..is表示继承,通过复制代码的方式实现继承

contract  A{
}
contract  B is A{
}

3.如果父合约与构造函数,那么派生合约需要调用父合约的构造函数,如果有参数,派生合约需要提供参数调用父合约构造函数。

形式一:

contract  B is A(uint a){
}
//其中a为传递给父合约A的参数

形式二:

contract B is A{
    constructor(uint a) A(a) public{
    }
    }

3.多重继承:

is后接多个合约,基类合约在is后的顺序很重要。继承顺序原则是从最接近基类到最接近派生类。

4.抽象合约:

合约在没有函数体(实现)的函数

合约不能通过编译,只能继承

5.接口:

关键字:interface

里面的函数必须是未实现的。函数不能继承其他合约或者接口。不能定义构造器,变量,结构体,枚举类。

抽象合约和接口的作用主要是用做基类。


1.库其实就是一个特殊的合约:

可以像其他合约一样进行部署,但是没有状态变量,不能存以太币。

2.可重用:

部署一次,在不同的合约内反复使用。节约gas,相同功能的代码不用重复部署

3.定义库、使用库:

定义:

library  my{
    myfunction();
}

使用:

先导入:import “./my”,,然后my.myfunction();

4.网上好用的常用库:

openzeppelin,github地址是:

https://github.com/Openzeppelin/openzeppelin-solidity


另外还有:ethereum-libraries,dapp-bin,Stringutils等

回调函数

定义:无名称。无参数,无返回值的函数

一个合约可以有一个回退函数。当给合约转以太币时,需要有payable回退函数。如果调用合约时,没有匹配上任何函数,就会调用回退函数。

未完。。。



版权声明:本文为qq_34493908原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。