Solidity语言学习(4)——值类型与引用类型

由于Solidity是一个静态类型的语言,所以编译时需明确指定变量的类型(包括 本地变量状态变量),solidity编程语言提供了一些基本类型(elementary types)可以用来组合成复杂类型。

类型可以与不同运算符组合,支持表达式运算,你可以通过表达式的执行顺序(Order of Evalution of Expressions)来了解执行顺序

值类型(Value Type)

值类型包含

  • 布尔(Booleans)
  • 整形(Integer)
  • 地址(Address)
  • 定长字节数组(fixed byte arras)
  • 有理数和整形(Rational and Integer Literals,String literals)
  • 枚举类型(Enums)
  • 函数(Function Types)

为什么会叫值类型,是因为上述这些类型在传值时,总是值传递。比如在函数传参数时,或进行变量赋值时。

引用类型(Reference Types)

复杂类型,占用空间较大的。在拷贝时占用空间较大。所以考虑通过引用传递。常见引用类型有:

  • 不定长字节数组(bytes)
  • 字符串(string)
  • 数组(Array)
  • 结构体(Structs)

布尔(Booleans)

bool:可能的取值为常量true 和 false
支持的运算符:

  • ! 逻辑非
  • && 逻辑与
  • || 逻辑或
  • == 等于
  • != 不等于
    注意:&& 和 || 是短路运算符,他只会先执行前面的,如果无法判定结果才会执行后面的,比如 f(x) || g(y),如果f(x)已经判定为false,则结果为false,不会再执行g(y);同理 f(x) && g(y) 若 f(x) 判定为真,则 g(y)也不会再执行。

整形(Integer)

int/uint: 变长的有符号或无符号整形.变量支持的步长以8递增,支持从 uint8 到 uint256,以及 int8 到 int256。需要注意的是,uint 和 int 默认代表的是 uint256 和 int256(2^256,-2^128~2^128)

支持的运算符:

  • 比较: <=, <, ==, != ,>=, >,返回值为bool类型。
  • 位运算符: & , | , (^异或) , (~非).
  • 数学运算: + , -, * , /, (%求余), ( **幂)

整数除法总是截断的,但如果运算符是字面量,则不会截断(后面会进一步提到).另外除 0 会抛出异常.

pragma solidity ^0.4.0;

// simple store example
contract simpleStorage{
      uint valueStore;

      function add(uint x,uint y) returns (uint z){
            z = x + y;
       }

       function divide() returns (uint z ){
             uint x = 1;
             uint y = 2;
             z = x / y ;
        }//将输出0(截断了后面的小数)
 }

整数字面量

整数字面量,由包含0-9的数字序列组成,默认被解释为十进制。在solidity中不支持八进制,前导0会被默认忽略。
小数由.组成,在他的左边或者右边至少要包含一个数字如1. , .1 , 1.3均为有效小数
字面量本身支持任何精度,也就是可以不会计算溢出,或除法截断。但当它被转换成对应的非字面量类型,如整数或小数。或者将他们与非字面量进行计算,就不能保证精度了。

pragma solidity ^0.4.0;

contract IntegerLiteral{
     function integerTest() return (uint, uint,uint){
          //超出运算字长
          var i = (2**800 + 1) - 2**800;
          var j = 1/3*3;
          // 小数运算
          var k = 0.5*8return(i,j,k)
      }
}

总之来说就是,字面量怎么都计算都行,但一旦转为对应的变量后,再计算就不保证精度了。

地址

地址:以太坊地址的长度,大小20个字节,160位,所以可以用一个uint160编码。地址是所有合约的基础,所有的合约都会继承地址对象,也可以随时将一个地址串,得到对应的代码进行调用。当然地址代表一个普通账户时,就没有这么多的的功能了。

0.5.0以后合约不是从地址类型中派生出来的了,但是仍然可以显示地转换为地址。

支持的运算符:

  • <= , < , ==, != , >= , >

地址类型的成员:
属性: balance
函数:send(),call(),delegatecall(),callcode()。

地址字面量:
十六进制的字符串,凡是能通过地址合法性检查(address checksum test),就会被认为是地址,
(地址合法性检查:为防止录入地址有误,一种格式化地址后来确认地址有效性的方案)。需要注意的是39到41位长的没有通过地址合法性检查的,会提示一个警告,但会被视为普通的 有理数 字面量。

balance
通过它能够得到一个地址的余额。

pragma solidity ^0.4.0;

contract addressTest{
      function getBalance(address addr) returns (uint){
             return addr.balance;
      }
}  

this
如果只是想得到当前合约的余额,也可以这样写

pragma solidity ^0.4.0;

contract addressTest{

     function getBalance() returns (uint) {
          return this.balance;
       }
}

原因是对于合约来说,地址代表的就是合约本身,合约对象默认继承自地址对象,所以内部有地址的属性。

地址的方法 send()
用来向某个地址发送货币(货币单位为 wei)。

pragma solidity ^0.4.0;

//请注意这个仅仅是Demo,请不要用到正式环境
contract PayTest{
       //得到当前合约余额
       function getBalance() returns (uint) {
             return this.balance;
        }

        //向当前合约存款
        function deposit() payable returns(address addr,uint amount, bool success){
              //msg.sender 全局变量,调用合约的发起方
              //msg.value 全局变量,调用合约的发起方转发的货币量,以wei为单位
              //send() 执行的结果
              return (msg.sender, msg.value,this.send(msg.value));
         }
}

这个合约实现的是充值。this.send(msg.value) 意指向合约自身发送 msg.value 量的以太币。msg.value是合约调用方附带的以太币。
send()方法执行时有一些风险
1. 调用递归深度不能超过1024.
2. 如果gas不够,执行将会失败。
3. 所以使用这个方法一定要检查成功与否。或为保险起见,货币操作时要使用一些最佳实践。

如果执行失败,将会回撤所有交易,所以务必留意返回结果。

现在地址的方法增加了一种更加安全的方法 x.transfer(coin) , 表示向x这个转入coin个以太币

call(),callcode()和delegatecall()
为了同一些不支持ABI协议的进行直接交互(一般的web3.js,solidity都是支持的)。可以使用call()函数,用来向另一个合约发送原始数据。参数支持任何类型任意数量。每个参数会按规则(规则是按ABI)打包成32字节并一一拼接到一起。

call()方法支持ABI协议[ABI]定义的函数选择器。如果第一个参数恰好4个字节,在这种情况下,会被认为根据ABI协议定义的函数器指定的函数签名[ABI] 。所以如果你只是向发送消息体,需要避免第一个参数是4个字节。

call方法返回一个bool值,以表面执行成功还是失败。正常结束返回true,异常终止返回false。我们无法解析返回结果,因为这样我们得事前知道返回的数据的编码和数据大小(这里的潜在假设 是不知道对方使用的协议格式,所以也不知道会返回的结果如何解析)。

同样我们也可以使用 delegatecal(),它与call 方法的区别在于,仅仅是代码会执行,而其它方面,如(存储,余额等)都是用的当前的合约数据。delegatecall() 方法的目的是用来执行另一个合约中的工具库。所以开发者需要保证两个合约中的存储变量能兼容,来保证delegatecall()能顺利执行。

在homestead阶段之前,仅有一个受限的多样的callcode()方法可用,但并未提供对 msg.sender, msg.value 的访问权限。

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

关于 call() 函数究竟发的什么消息体,函数选择器究竟怎么用,参见这里

上述函数都是底层的函数,使用时要异常小心。当调用一个未知的,可能是恶意的合约时,当你把控制权交给它,它可能回调回你的合约,所以要准备好在调用返回时,应对你的状态变量可能被恶意篡改的情况。

相关的链接

  1. 更多关于地址的由来,UTXO等
  2. 关于地址合法性检查
  3. 关于以太币支付(payable等)
  4. 关于ABI协议的详细说明

猜你喜欢

转载自blog.csdn.net/weixin_42595515/article/details/81903337
今日推荐