Solidity L1-数据类型

2019/03/20 区块链

Solidity是以太坊官方推荐的编程语言,它是一门静态类型语言。与C++等静态类型语言类似的,Solidity的数据类型分为两种,分别是值类型以及引用类型。

概要

Solidity值类型包括布尔类型整型定长浮点型定长字节数组有理数和整数常量字符串常量十六进制常量枚举函数类型地址类型地址常量

Solidity引用类型包括数组结构体映射

定长字节数组

定长字节数组是指声明数组的时候指定固定空间,与C++的声明有很明显的区别:

byte bt0;
bytes1 bt1 = "0x01";
bytes2 bt2 = "ab";

关键字从bytes1bytes32,以步长1递增。byte相当于bytes1。定长字节数组有一个属性length

有理数和整数常量

与C++不同,Solidity中不同类型之间没有办法进行运算。例如下面:

uint128 a = 1;
uint128 b = 2.5 + a + 0.5; // error

字符串常量

字符串常量支持转义字符,不过不能进行任何运算,例如通过“+”进行的字符串拼接操作也是不允许的。

枚举

pragma solidity ^0.4.0;
contract test {
    enum ActionChoices {
        GoLeft, GoRight, GoStraight, SitStill
    };
    ActionChoices choice;
    ActionChoices constant defaultChoice = ActionChoices.GoStraight;

    function setGoStraight() {
        choice = ActionChoices.GoStraight;
    }
    function getChoice() returns (ActionChoices) {
        return choice;
    }
    function getDefaultChoice() returns (uint) {
        return uint(defaultChoice)
    }
}

函数类型

contract FunctionSelector {
    function select(bool useB, uint x) returns (uint z) {
        var f = a;
        if (useB)
            f = b;
        return f(x);
    }
    function a(uint x) returns (uint z) {
        return x * x;
    }
    function b(uint x) returns (uint z) {
        return 2 * x;
    }
}

函数类型分为两种,分别是内部函数外部函数。内部函数只能在当前合约内被调用,而外部函数由地址和函数方法签名两部分组成,外部函数可以作为外部函数调用的参数或返回值。

函数类型的定义是:

function (<parameter types>) {internal|external|public|private} [pure|constant|view|payable] [returns (return types)]

Solidity对函数和状态变量提供了四种可见性。分别是externalpublicinternalprivate

  1. internal: 函数只能通过当前合约或者继承合约访问。
  2. external: 函数只能通过外部的方式调用。
  3. public: 函数可以通过内部或外部来调用。
  4. private: 函数只能通过当前合约访问。

下面的例子区分external与public的不同:

pragma solidity^0.4.12;

contract Test {
    uint[6] x = {1, 2, 3, 4, 5, 6}; 
    function test(uint[6] a) public returns (uint) {
        return a[2] * 2;
    }
    function test2(uint[6] a) external returns (uint) {
        return a[2] * 2;
    }
    function calltest() {
        test(x);
        // this.test(x); // 允许。
    }
    function calltest2() {
        this.test2(x);
        // test2(x); // 这样写会错误,因为external函数只能外部调用。
    }
}

外部调用test函数比调用test2函数花费更多的gas,因为对于public函数,EVM在调用的时候需要将参数copy到内存中,而对于external函数则不需要内存,而是直接读取calldata。

因此,当函数仅需要外部调用的时候,使用external更好。

地址类型

地址类型是一个值类型,占用20字节。

address addr = 0x1111111111111111111111111111111111111111;

balance属性以及transfer()函数

address x = 0x123;
address myAddress = this;
if(x.balance < 10 && myAddress.balance >= 10)
    x.transfer(10);
// x接收10 wei以太币,而不是发送10 wei以太币。

send()函数

与transfer对应。

call(),delegatecall()

call()函数向另一个合约发送原始数据,而delegatecall()方法用来执行另一个合约中的库代码。

pragma solidity ^0.4.0

contract AddrTest {
    event logdata(bytes data);
    function() payable {
        logdata(msg.data);
    }
    function getBalance() returns (uint) {
        return this.balance;
    }
    uint score = 0;
    function setScore(uint s) public {
       score = s;
    }
    function getScode() returns (uint) {
        return score;
    }
}

contract CallTest {
    function deposit() payable {
    }
    event logSendEvent(address to, uint value);
    function transferEther(address toWho) payable {
        // toWho接收10 wei以太币。
        toWho.transfer(10);
        logSendEvent(toWho, 10);
    }
    // 发送原始数据。
    function callNoFunc(address addr) returns (bool) {
        // call函数返回一个bool值,但无法获取结果数据。
        return addr.call("tinyxiong", 1234);
    }
    // 发送函数。
    function callFunc(address addr) returns (bool) {
        // keccak256是散列函数。
        bytes4 methodID = bytes4(keccak256("setScore(uint256)"));
        return addr.call(methodID, 100);
    }
    function getBalance() returns (uint) {
        return this.balance;
    }
}

判断合约地址与外部账号地址

一种方法是使用EXTCODESIZE:

function isContract(address addr) internal view returns (bool) {
    uint256 size;
    assembly {
        size := extcodesize(addr)
    }
    return size > 0;
}

另一种方法是使用web3.eth.getCode()。如果参数是外部账号地址,则返回”0x”,如果参数是合约,则返回对应的字节码。

Search

    Table of Contents