智能合约(三):Solidity变量类型:值类型

栏目: C · 发布时间: 6年前

内容简介:本文完,获取更多资讯,敬请关注区块链工程师。

1

目录

☛值类型和引用类型的区别

☛布尔类型(bool)

☛整型(int、uint)

☛定点型小数(fixed、ufixed)

☛地址类型(address)

☛定长字节数组(bytes1,bytes2,bytes3,...,bytes32)

☛有理数和整数字面量

☛地址字面量

☛字符串字面量

☛十六进制字面量(hex)

☛枚举类型(enum)

2

值类型和引用类型的区别

Solidity变量类型分为两大类——值类型、引用类型

值类型:变量的存储空间存的是变量的数据
引用类型:变量的存储空间存的是变量数据所在的存储空间的地址

注意:值传递和引用传递。值类型的变量,赋值是简单的值传递,即两个变量占有独立的存储区域。引用类型赋值传递的是变量的引用,即两个变量指向同一存储区域

3

值类型——布尔(bool)

bool: 只有两种值true和false(默认false)。

3.1 支持的运算符:

▪ ! 逻辑非

▪ && 逻辑与

▪ || 逻辑或

▪ == 等于

▪ != 不等于

3.2 实例

bool a = true;bool b = !a;// a == b -> false// a != b -> true// a || b -> true// a && b -> false

逻辑与(&&)和逻辑或(||)都遵循短路原则,即如果根据前一个表达式可以得到运算结果,则不会执行后面的表达式。

4

值类型——整型(int/uint)

▪ int(m):有符号整数

▪ uint(m):无符号整数

▪ m关键字取值为8~256步幅是8 ,表示在内存中2进制的位数,控制了整数的取值范围,不写默认为256。

▪ uint和int分别是uint256和int256的别名。

▪ m一定要是8的整数倍

4.1 操作

比较:<=,<,==,!=,>=,>(结果为bool)位操作符:&,|,^(按位异或),~(按位取反)算术运算符:+, - ,一元 - ,一元 +,*,/,%(取余),**(幂),<<(左移),>>(右移)

注意:

▪ 除法总是截断,但如果两个运算符都是常量(或常量表达式),则它不会截断。

▪ 除零和取余有零引发异常。

▪ 左移几位和右移几位相当于乘以或者除以2的几次方,如果参数为负数的话会引发异常。

▪ 在Solidity中不支持八进制。

5

值类型——定点数

5.1 定点小数

到现在为止还没有被solidity完全支持,可以被声明,不能被赋值也不能用定点数赋值fixed/ufixed 各种大小的有符号和无符号定点小数,ufixedMxN and fixedMxN关键字M代表定点数占用的二进制位数,N代表定点数能表示多少位小数,M必须是8-256之间的,以8为步幅的整数,N必须是0-80之间的整数,ufixed 和fixed 默认为ufixed128x18和fixed128x18。

▪ 比较运算: <=, <, ==, !=, >=, > (结果为bool)

▪ 算数运算: +, -, 一元-, 一元 +, *, /, % 取余

6

值类型——地址类型(address)

在了解地址之前需要知道一个名词---ABI协议:

Application Binary Interface 应用二进制接口,是从区块链外部与合约进行交互以及合约与合约间进行交互的一种标准方式。ABI是以太坊的一种合约间调用时的一个消息格式。类似Webservice里的SOAP(Simple Object Access Protocol简单对象访问协议)协议一样。

常见格式:

[
   {		      "constant": false,	      "inputs": [
	   {				                "name": "index",	                "type": "uint8"
	   },
	   {		                 "name": "s",                 "type": "string"
	   }
	],		       "name": "removeItem",		       "outputs": [
	    {				               "name": "",				               "type": "uint256"
	    }
       ],		       "payable": false,		       "stateMutability": "nonpayable",		       "type": "function"
    }
]

地址:保存一个20字节值,使用40位16进制数表示。地址类型也有成员,并作为所有合约的基础。

6.1 地址成员

▪ 以wei位单位返回该地址的余额

<address>.balance(uint256)

▪ 从当前合约地址中给调用函数的地址账户转入amounts数量(以wei为单位)的金额,即从当前合约转账到某账户地址。如果执行失败,将抛出异常,需要支付2300gas的费用,不可以调整。

<address>.transfer(uint256 amount)

其中address就是要把合约中的币转到哪个账号地址。

范例:

pragma solidity ^0.4.24;

contract AddressExample {        //定义一个接受以太币的函数往合约里充值
    function AddressExample()payable{}       //函数的参数分别是划转到某账号地址以及转账数量
    function giveEthersTo(address _toAccount,uint amount){         if (this.balance >=amount){
            _toAccount.transfer(amount);
        }
    }        function getBalance()view returns(uint){                return this.balance;
    }        //定义一个匿名的回退函数接收异常情况下退回的以太币
    function() payable{}
}

▪ 如果合约地址调用transfer,那么需要合约地址有payable类型的回退函数,并且会随着转账一块执行回退函数中的代码,如果因为回退函数中的代码执行把gas消耗光了,EVM会抛出异常,转账也会被回退。

▪ send是低级对等的转账。执行失败,不会抛出异常,会返回false,需要支付2300gas的费用,不可以调整。推荐使用transfer而不使用send。

<address>.send(uint256 amount) returns (bool)

▪ call(), callcode() 和 delegatecall() 函数。

<address>.call(...) returns (bool)<address>.callcode(...) returns (bool)<address>.calldelegate(...) returns (bool)

为了和非ABI协议;也就是定义操作函数签名,参数编码,返回结果编码等的合约进行交互,可以使用call() 函数, 它用来向另一个合约发送原始数据,支持任何类型任意数量的参数,每个参数会按规则(ABI协议)打包成32字节并一一拼接到一起。一个例外是:如果第一个参数恰好4个字节,在这种情况下,会被认为根据ABI协议定义的函数器指定的函数签名而直接使用。如果仅想发送消息体,需要避免第一个参数是4个字节。如下面的例子:

address nameReg = 0x72ba7d8e73fe8eb666ea66babc8116a41bfb10e2;nameReg.call("register", "MyName");nameReg.call(bytes4(keccak256("fun(uint256)")), a);

call()

是一个底层的接口,用来向一个合约发送消息,也就是说如果你想实现自己的消息传递,可以使用这个函数。函数支持传入任意类型的任意参数,并将参数打包成32字节,相互拼接后向合约发送这段数据。 call函数返回一个bool值,以表明执行成功与否。正常结束返回true,异常终止返回false。但无法获取到结果数据。还可以提供.gas()修饰器进行调用:

namReg.call.gas(1000000)("register", "MyName");

类似还可以提供附带以太币:

nameReg.call.value(1 ether)("register", "MyName");

修饰器可以混合使用,修饰器调用顺序无所谓。

nameReg.call.gas(1000000).value(1 ether)("register", "MyName");

delegatecall()

call与delegatecall的功能类似,区别仅在于后者仅使用给定地址的代码,仅仅是代码会执行,其它信息则使用当前合约(如存储,余额等等)。

函数的设计目的是为了使用存储在另一个合约的库代码。

所以开发者在提供这样的库时,就要如何安排存储来达到这样的目的。

delegatecall()方法的目的是用来执行另一个合约中的库代码。所以开发者需要保证两个合约中的存储变量能兼容,来保证delegatecall()能顺利执行。

callcode()

未提供对msg.sender,msg.value的访问权限。

call和callcode以及delegateCall的区别:

三者的区别主要体现在三个方面,作用域(上下文)、msg.sender、以及this。

以下是比较示例:

contract D {
  uint public n;
  address public sender;

  function callSetN(address _e, uint _n) {        _e.call(bytes4(sha3("setN(uint256)")), _n); // 执行结果:改变了E中的变量值, 而当前的同名变量并没改变。 
  }

  function callcodeSetN(address _e, uint _n) {        _e.callcode(bytes4(sha3("setN(uint256)")), _n); // 执行结果:当前状态变量被改变,而E的并没改变。 
  }

  function delegatecallSetN(address _e, uint _n) {        _e.delegatecall(bytes4(sha3("setN(uint256)")), _n); //执行结果同callcode
  }
}

contract E {
  uint public n;
  address public sender;

  function setN(uint _n) {
    n = _n;
    sender = msg.sender;        // 如果D用call的方式调用,则msg.sender是D的合约地址
    //如果D用callcode的方式调用,则msg.sender不会被获取
    //如果D用delegateCall的方式调用,msg.sender也不能被获取
    // 如果是C中的函数foo()间接调用了. msg.sender则是C

    // 如果D用callcode或者delegateCall调用,则this指向D
  }
}

contract C {
    function foo(D _d, E _e, uint _n) {                _d.delegatecallSetN(_e, _n);
    }
}

上面的这三个方法call(),delegatecall(),callcode()都是底层的消息传递调用,最好仅在万不得已才进行使用,因为他们破坏了Solidity的类型安全。 .gas() 在call(), callcode() 和 delegatecall() 函数下都可以使用, delegatecall()不支持.value()。

pragma solidity ^0.4.24;

contract Person{    uint age = 10;     
    
    function increaseAge(string name, uint num) returns (uint){                return ++age;
    }    
    function getAge()returns (uint){                return age;
    }

}

contract CallTest{    
    function callByFun(address addr)returns (bool){
        bytes4 methodId = bytes4(keccak256("increaseAge(string,uint256)"));                return addr.call(methodId,"jack", 1);
    }
}

注意:

合约中使用的this表示当前合约地址;

所有的合约对象都可以被转成地址类型,查询当前合约的余额。

address(this).balancethis.balance

7

值类型——定长字节数组

bytes1, ... ,bytes32,允许值以步长1递增。byte默认表示bytes1。

7.1 操作

▪ 比较:<=,<,==,!=,>=,>(评估为bool)

▪ 位运算符:&,|,^(按位异或),~(按位取反),<<(左移),>>(右移)

▪ 索引访问:如果x的类型为bytesI,则0 <= k <I的x [k]返回第k个字节(只读)。

7.2 成员

.length产生字节数组的固定长度(只读)。

8

值类型——有理数和整数字面量

整数常量和有理数常量均支持科学计数法。基数可以是小数,指数必须是整数。 例如2e10,-2e10,2e-10,2.5e10。

8.1 有理字面量(特指小数字面量)

▪ 有理数字面量带一个.,在.的两边至少要有一个数字,有效的表示如下1.,.1,1.2

▪ 不允许位运算,小数不能用作指数

▪ 有理数本身支持任意精度,任何运算不会发生溢出或除法截断,当被转换成对应的其他类型,或者与其他类型运算时,不再保证精度。

8.2 整数字面量

▪ 由一系列0-9的数字组成的10进制数,存在十六进制表示形式“0x”开头,不存在以“0”八进制的表示形式。

//十六进制表示0x692a70d2e424a56d2c6c27aa97d1a86395877b3a

▪ 整数常量表达式的除法运算,不做截断处理,结果是有理数。

注意:

数字字面量表达式一旦其中含有非字面量表达式,它就会被转为一个非字面量表达式。

pragma solidity ^0.4.24;
contract IntegerLiteralConvert{    function literalTest(){
    uint128 a = 1;        //uint128 b = 2.5 + a + 0.5;
    //2.5+a不能转换成一个非字面量表达式
  }
}

9

值类型——地址字面量

地址字面量表现形式其实就是十六进制整数字面量,如果能够通过地址校验,就会被认为是地址类型,如果不通过则它表示的是一个整数。例如:

0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF

就是一个地址类型的字面量,如果长度为39~40,没有通过地址校验的十六进制整数字面量,会被视为有理数常量,并且会产生一个warning(警告提示)。

10

值类型——字符串常量

字符串字面量是用双引号或单引号(“foo”或‘bar’)编写,长度可变。 它们不像 C语言 那样默认以0结尾; “foo”代表三个不是四个字节。 它们可以隐式转换为bytes1,…,bytes32。

pragma solidity ^0.4.24;contract StringLiteralsTest{        bytes15 public name;        function setName(){                name = 'liankuaixueyuan';
    }
}

11

值类型——十六进制字面量

十六进制字面量,以关键字hex打头,后面紧跟用单或双引号包裹的字符串。如hex"001122ff"。在内部会被表示为二进制流,与字符串的存储形式相同,可以与字符串进行隐式转换,通过下面的例子来理解下是什么意思:

pragma solidity ^0.4.24;

contract HexLiteral{        string name;
    bytes nameBytes;        function setName()public{
        name = hex"6c69616e6b7561697875657975616e";
        nameBytes = hex"6c69616e6b7561697875657975616e";            //nameBytes = hex"a";
      //由于一个字节是8位,所以一个hex是由两个[0-9a-z]字符组成的。所以var b = hex“A”;不是成双的,转字符串是会报错的

    }        function getName() public view returns (bytes,string){          return (nameBytes,name);
  }
}

12

值类型——枚举

枚举类型是在Solidity中的一种用户自定义类型。

pragma solidity ^0.4.24;

contract test {
    enum ActionChoices { GoLeft, GoRight, GoStraight, SitStill }
    ActionChoices choice;
    ActionChoices constant defaultChoice = ActionChoices.GoStraight;        function setGoStraight() public {
        choice = ActionChoices.GoStraight;  // 这里通过访问下标的形式得到相同的结果    
        // choice = ActionChoices(2);
        
    }        function getChoice() public view returns (ActionChoices) {                return choice;
    }        function getDefaultChoice() public view returns (uint) {                return uint(defaultChoice);
    }
}

注意:

▪ 枚举可以显式的转换与整数进行转换,但不能进行隐式转换。显式的转换会在运行时检查数值范围,如果不匹配,将会引起异常。

▪ 枚举类型应至少有一名成员。

本文完,获取更多资讯,敬请关注区块链工程师。

智能合约(三):Solidity变量类型:值类型

来源: 链块学院

本文由布洛克专栏作者发布,代表作者观点,版权归作者所有,不代表布洛克科技观点

——TheEnd——

关注“布洛克科技”

智能合约(三):Solidity变量类型:值类型


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Head First 设计模式(中文版)

Head First 设计模式(中文版)

弗里曼 / O'Reilly Taiwan公司 / 中国电力出版社 / 2007-9 / 98.00元

《Head First设计模式》(中文版)共有14章,每章都介绍了几个设计模式,完整地涵盖了四人组版本全部23个设计模式。前言先介绍这本书的用法;第1章到第11章陆续介绍的设计模式为Strategy、Observer、Decorator、Abstract Factory、Factory Method、Singleton,Command、Adapter、Facade、TemplateMethod、I......一起来看看 《Head First 设计模式(中文版)》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

MD5 加密
MD5 加密

MD5 加密工具