Solidity值类型与各种图解
区块链中级.以太坊开发
From:JamesZou & 传智播客研究院 & 传智播客区块链
一.值类型和引用类型
1.值类型
- 值类型 是指变量在赋值过程中是将数据完整的拷贝一份,再赋值给新的变量,这种方式需要开辟新的内存空间,效率较低,两个变量完全独立,修改一个不会影响另外一个。
- 布尔(Booleans)
- 整型(Integer)
- 地址(Address)
- 定长字节数组(fixed byte arrays)
- 有理数和整型(Rational and Integer Literals,String literals)
- 枚举类型(Enums)
- 函数(Function Types)
思考:下面这个代码中的 luckNum 最后等于多少?
pragma solidity ^0.4.25;
contract ValueType{
function testValueType() public view returns(uint){
uint luckNum = 101;
uint loveNum = luckNum;
changeValue(luckNum);
return luckNum;
}
function changeValue(uint _num) private{
_num += 100;
}
}
2.引用类型
- solidity没有指针,对于复杂的结构进行高效传递方式是使用关键字
storage
进行修饰。 - 复杂类型,占用空间较大的。在拷贝时占用空间较大。所以考虑通过引用传递。常见的引用类型有:
- 字符串(string)
- 不定长字节数组(bytes)
- 数组(Array)
- 结构体(Structs)
二.数据类型
1.值类型详介
1.1 布尔
bool flag1 = true;
bool flag2 = false;
1.2 整型
-
int(有符号整型,有正有负)
-
uint(无符号整型,无负数)
-
以8位为区间,支持int8,int16,int24 至 int256,uint同理。
int默认为int256,uint默认为uint256
function intTest() public returns(int){
int8 i = 10;
int j = 10;
return i + j;
}
1.3 地址
1.3.1 概念
- 以太坊地址的长度,大小
20个字节
,每个字节8位,20 * 8 =160位
,所以可以用一个uint160
编码。地址是所有合约的基础,所有的合约都会继承地址对象,通过合约的地址串,调用合约内的函数。
1.3.2 运算符
描述 | 符号 |
---|---|
比较运算符 | <=,<,==,!=,>=,> |
1.3.3 操作
属性/方法 | 含义 | 备注 |
---|---|---|
balance(uint256) | 获取余额(wei) | 属性,其余的都是方法 |
transfer(uint256 value) | 给 address 转账 value(Wei),失败会抛异常 | 建议使用 |
send(uint256 value) | 和 transfer 类似,transfer 更常用,失败会返回false | 不建议使用 |
call | 合约内部调用合约 | |
delegatecall | 调底层代码,别用 | |
callcode | 调底层代码,别用 |
- call 是一个底层的接口,用来向一个合约发送消息,也就是说如果你想实现自己的消息传递,可以使用这个函数。函数支持传入任意类型的任意参数,并将参数打包成32字节,相互拼接后向合约发送这段数据。
pragma solidity ^0.4.25;
contract Lover{
string outerMsg;
function(){
outerMsg = string(msg.data);
}
function getFail() returns (string){
return outerMsg;
}
}
contract CallLover{
function callData(address addr) returns (bool){
return addr.call("i love Ether ~~!");
}
}
- call与delegatecall的功能类似,区别仅在于后者仅使用给定地址的代码,其它信息则使用当前合约(如存储,余额等等)
注意:在 solidity 源码中,address 不需要加双引号。但在 Remix 的对话界面中输入 address 时,务必加上双引号,否则会报错
1.3.4 余额
- 返回指定地址的余额
pragma solidity ^0.4.25;
contract addressTest{
function getBalance(address addr) constant public returns (uint){
return addr.balance;
}
}
-
合约地址 (this)
如果只是想返回当前合约账户的余额,可以使用
this
指针,this
表示合约自身的地址
pragma solidity ^0.4.0;
contract addressTest{
function getBalance() constant public returns (uint){
//return addr.balance;
return this.balance; // <<----此处使用this代替
}
}
-
转账(send,transfer)
send和transfer函数提供了由合约向其他地址转账的功能。
对比项 | send | transfer | 备注 |
---|---|---|---|
参数 | 转账金额 | 转账金额 | wei单位 |
返回值 | true/false | 无(出错抛异常) | transfer更安全 |
pragma solidity ^0.4.25;
contract TransferTest {
function transfer123(address addr) payable {
addr.transfer(msg.value);
}
}
- call 方法 (略)
1.4 字节数组
1.4.1 定长字节数组
-
solidity内置了一些数组的数据类型:(和go语言做一下对比, var [8] byte),完全只读
-
bytes1
, … ,bytes32
,允许值以步长1递增。 - byte默认表示bytes1,byte是类型,bytes是类型,bytes1是内置数组
- bytes1只能存储1个字节,即8位的内容,bytes2最多只能存储2个字节,即16位的内容。以此类推…
- 长度可以读取 length
- 长度不可以修改
- 可以通过下标访问
- 内容不可修改
-
-
内置方法:length() -> 返回数组长度
-
存储方式:16进制ascii码
pragma solidity ^0.4.25;
//bytes1
contract fixedArray {
/*
1. 长度可以读取 length
2. 长度不可以修改
3. 可以通过下标访问
4. 内容不可修改
*/
//bytes1 ... bytes32
//bytes1 b1 = "xy";
bytes2 b2 = "xy";
bytes3 public b3 = "xy";
uint public len = b3.length;
//b3.length = 10;
bytes8 b8 = "12345678";
//b8_0返回0x31,即十进制的数字1的ascii值(3*16+1=49)
bytes1 public b8_0 = b8[0];
//b = "HELLO";ERROR,定义之后不可修改
//b8[1] = "0";
//b8= "4567";
}
- 运算符支持
描述 | 符号 |
---|---|
比较运算 | <=,<,==,!=,>=,> |
位运算符 | &,|,^(异或),~非 |
下标访问 | [0,n),n表示长度 |
1.4.2 不定长字节数组
-
bytes:不定长度的字节数组 (Dynamically-sized byte array)
bytes类型可以使用 push() 方法,具有 length 属性
string public name = "itheima.com";
bytes public g = 0x6c697975656368756e;
// 初始化一个两个字节空间的字节数组
bytes public name = new bytes(2);
- string: 动态长度的UTF-8编码的字符类型(非值类型),它其实是一个特殊的可变字节数组
string public str01 = "itheima.com";
- solidity中字符串不像JS中那样,没有length()方法,须转换成字节数组才能使用length属性来获得长度
function getStrLegnth(string _str) constant returns (uint) {
return bytes(_str).length; // 强制转换string为bytes
}
- 一个好的使用原则是:
bytes用来存储任意长度的字节数据,string用来存储任意长度的UTF-8编码 的字符串数据。
如果长度可以确定,尽量使用定长的如byte1到byte32中的一个,因为这样更省空间。
1.5 枚举类型
- 枚举类型是在Solidity中的一种用户自定义类型。
- 枚举可以显示的转换与整数进行转换,但不能进行隐式转换。显示的转换会在运行时检查数值范围,如果不匹配,将会引起异常。
- 枚举类型应至少有一名成员,枚举元素默认为uint8,当元素数量足够多时,会自动变为uint16,第一个元素默认为0,使用超出范围的数值时会报错。
pragma solidity ^0.4.0;
contract test {
enum WeekDays {
Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
}
WeekDays currentDay;
WeekDays defaultday = WeekDays.Sunday;
function setDay(weekDays _day) public {
currentDay = _day;
}
function getDay() public view returns(uint256) {
return uint256(currentDay);
}
function getDefaultDay() public view returns(uint256) {
return uint256(defaultday);
}
}
1.6 函数类型
1.6.1 函数修饰符
- 函数类型也就是我们所说的函数,本身也是一个特殊的变量,它可以
当做变量赋值
,当做函数参数传递
,当做返回值
。
修饰符 | 说明 |
---|---|
public | 公有,任何人(拥有以太坊账户的)都可以调用 |
private | 私有, 只有智能合约内部可以调用 |
external | 仅合约外部可以调用,合约内部需使用this调用 |
internal | 仅合约内部和继承的合约可以调用 |
view/constant | 函数会读取但是不会修改任何contract的状态变量 |
pure | 函数不使用任何智能合约的状态变量 |
payable | 调用函数需要付钱,钱付给了智能合约的账户 |
returns | 返回值函数声明中使用 |
注意,所有在合约内的东西对外部的观察者来说都是可见,将某些东西标记为private
仅仅阻止了其它合约来进行访问和修改,但并不能阻止其它人看到相关的信息。
1.6.2 匿名函数
-
一个合约可以有且只有一个匿名函数,此函数不能有参数,也不能有任何返回值,当我们企图去执行一个合约上没有的函数时,那么合约就会执行这个匿名函数。
当合约在只收到以太币的时候,也会调用这个匿名函数,而且一般情况下会消耗很少的gas,所以当你接收到以太币后,想要执行一些操作的话,你尽可以把你想要的操作写到这个匿名函数里,因为这样做成本非常便宜。
//如果想向合约转账,在合约中添加如下函数即可
function() payable {
//函数体什么都不填
}