首页 > 其他分享 >2、solidity数据类型以及函数

2、solidity数据类型以及函数

时间:2022-10-04 19:25:09浏览次数:45  
标签:函数 storage solidity 数据类型 mapping uint 数组 memory 类型

solidity数据类型分为以下几种:

1、整型

    • int(有符号整型,有正有负)
    • uint(无符号整型,无负数)
    • 以8位为区间,支持int8,int16,int24 至 int256,uint同理。 int默认为int256,uint默认为uint256

2、布尔类型

true或者false

3、地址类型

address: 20 字节长度的值(以太坊的地址),地址类型有很多成员变量,是所有合约的基础。

   地址类型的成员

  •    balance 和 transfer

   可以通过地址的balance属性来查看一个地址的余额,发送以太币(单位为:wei)到一个地址可以使用 transfer方法

address x = 0x123;
address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);

  注意:如果x是一个合约地址,它的代码(如果存在的话,更明确是为 fallback 函数)将会和 transfer 调用一起被执行(这是EVM的限制,是不可阻止的)。如果执行过程中gas不够或是失败,当前合约会终止抛出异常

  •      send

     send方法和transfer很相似,但是比transfer更低级。如果send失败,当前的合约不会中断,也不会抛出异常,会返回一个false。

    注意:使用send有一些风险:如果调用栈深度超过1024或是gas不够,所有的转让操作都会失败,为了更安全的以太币转移,如果用send就必须每次都要检查返回值,使用transfer方法会更好;

  •      call, callcode, delegatecall

     而且,call方法可以和没有依附于ABI上的合约进行交互,它提供了任意多个任意类型的参数。这些参数填充成32字节,并连接起来。有情况如果第一个参数被加密成4个字节,就会抛出异常,这种情况下,是不允许使用这个方法。

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

  call 返回一个boolean值,被调用函数终止返回true或是有EVM异常就返回false。不会返回访问的具体数据(对此,我们需要预先知道编码方式和大小)。

      同样的方式,delegatecall也可以使用,和call的唯一区别是 delegatecall会使用给定地址的代码。所有其他方面(存储,余额等)都是从当前合约中获取。delegatecall的作用是使用其他合约里的library代码。用户需要确保两个合约的存储布局适用于 deletegatecall的使用。

      call, delegatecall和call都是很低层次的方法,只有在实在没办法的时候才使用,这三个方法会破坏Solidity里的类型安全。

      三个方法都可以使用 .gas(),而 .value() 不适合deletegatecall.

      注意:所有的合约都继承了地址相关成员方法。所以可以使用 this.balance 来查询当前合约的余额

      ⚠️ 这些方法都是低层数的方法,使用的时候一定要小心。如果使用不当,任何未知的合约都可能别破坏。 你应该移交控制到可以通过回调到你自己合约的那个合约里,这样通过返回值就可以更新你自己的state变量

4、枚举类

enum WeekDays {
        Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
    }

5、 字节数组

定长字节数组

关键字有:bytes1, bytes2, bytes3, ..., bytes32byte 是 bytes1 的别名。

5.1 运算符:

  • 比较运算符:<=, <, ==, !=, >=, > (返回布尔型)
  • 位运算符: &, |, ^ (按位异或), ~ (按位取反), << (左移位), >> (右移位)
  • 索引访问:如果 x 是 bytesI 类型,那么 x[k] (其中 0 <= k < I)返回第 k 个字节(只读)。
  • 该类型可以和作为右操作数的任何整数类型进行移位运算(但返回结果的类型和左操作数类型相同),右操作数表示需要移动的位数。 进行负数位移运算会引发运行时异常。

    成员变量:

    • .length 表示这个字节数组的长度(只读).

5.2 可变字节数据

      bytes: 动态大小byte数组,不是一个值类型

     string:动态大小UTF-8编码string,不是一个值类型

     作为一个经验法则,对于任意长度的raw数据,使用bytes。对于任意长度的string(UTF-8)数据,使用string。如果可以限定bytes到一定长度,优先使用byte1到byte32,这32个byte类型开销更小

6、数组

   数组在编译期间固定大小或是声明为一个动态数组, 对于存储数组,数组里的元素类型是可以为任意的(可以是其他数组,mapping或是structs).对于内存数组,如果是一个public方法的实参,元素不能是mapping,而且必须是ABI类型。

       如果数组的长度为x, 元素类型为T,则可以写成 T[x],一个动态长度的数组,可以写成 T[]。例如,一个数组为5的动态数组,类型为uint,可以写成 uint[][5]。访问第二个uint中的第三个元素,可以使用 T[2][1] (数组下标从0开始)。bytes和string是特殊的数组,bytes类似于byte[],但是它在calldata里是紧凑存放。string等于bytes,但是不允许根据长度或下标访问。所以从廉价程度来说,相对于byte[],更优先使用bytes。

 分配内存数组

    在内存里创建一个可变长度数组可以使用new关键字,和存储空间数组相反,它不能通过 .length 重新设置长度。

pragma solidity ^0.4.0;

contract C {
    function f(uint len) {
        uint[] memory a = new uint[](7);
        bytes memory b = new bytes(len);
        // Here we have a.length == 7 and b.length == len
        a[6] = 8;
    }
}

  数组常量或是内联数组

     常量数组可以写成一种表达式,并且不可以赋值给一个变量。

复制代码
pragma solidity ^0.4.0;

contract C {
    function f() {
        g([uint(1), 2, 3]);
    }
    function g(uint[3] _data) {
        // ...
    }
}
复制代码

     常量数组是一个固定长度的内存数组,它的基本类型是给定元素的类型。[1,2,3]的类型为 uint8[3] memory ,因为包含的每个元素类型都为 uint8。 所以有必要把前面例子的第一个元素类型改成 uint。需要注意:固定长度的内存数组是不可以赋值给 动态长度的内存数组。下面的例子是错误的。

复制代码
pragma solidity ^0.4.0;

contract C {
    function f() {
        // The next line creates a type error because uint[3] memory
        // cannot be converted to uint[] memory.
        uint[] x = [uint(1), 3, 4];
}
复制代码

     这个限制现有有计划会在后续版本里去掉。

    成员

  •       length

         数组有个length成员来表示数组元素的个数。动态数组可以在存储空间(不是内存)里通过改变 length值来重新设置长度。

  •      push

        动态存储数组 和 bytes(不是string)可以使用push来增加一个元素到数组的尾部。这个方法将返回一个新的长度。

 

7、结构

    Solidity提供了一种用户自定义格式的新类型。例如:

pragma solidity ^0.4.11;

contract CrowdFunding {
    // Defines a new type with two fields.
    struct Funder {
        address addr;
        uint amount;
    }

    struct Campaign {
        address beneficiary;
        uint fundingGoal;
        uint numFunders;
        uint amount;
        mapping (uint => Funder) funders;
    }

    uint numCampaigns;
    mapping (uint => Campaign) campaigns;

    function newCampaign(address beneficiary, uint goal) returns (uint campaignID) {
        campaignID = numCampaigns++; // campaignID is return variable
        // Creates new struct and saves in storage. We leave out the mapping type.
        campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0);
    }

    function contribute(uint campaignID) payable {
        Campaign c = campaigns[campaignID];
        // Creates a new temporary memory struct, initialised with the given values
        // and copies it over to storage.
        // Note that you can also use Funder(msg.sender, msg.value) to initialise.
        c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value});
        c.amount += msg.value;
    }

    function checkGoalReached(uint campaignID) returns (bool reached) {
        Campaign c = campaigns[campaignID];
        if (c.amount < c.fundingGoal)
            return false;
        uint amount = c.amount;
        c.amount = 0;
        c.beneficiary.transfer(amount);
        return true;
    }
}
复制代码

   结构可以用在mapping和数组的内部,当然结构本身也可以包含mapping或是数组。虽然结构本身可以作为mapping的成员值,但是结构不可以包含它本身类型。这个约束是有必要的,是为了防止出现结构套结构,无限的循环套用。注意:结构是赋值给一个局部变量(默认为存储空间数据位置),不是拷贝结构,而是存储一个引用指向局部变量。当然,不用赋值给局部变量,也可以直接访问结构体的成员 campaigns[campaignID].amount = 0

8、Mapping

Mappings类型定义为 mapping(_KeyType => _ValueType),这里 _KeyType可以是除了mapping外的其他任意类型。 _ValueType 可以是任意类型,包括mapping。Mapping可以看成是一个哈希表,初始化每个存在的key,对应的value的值会初始化为所有的字节都为0。mapping的key事实上不是存在mapping中,只有key的keccak256 哈希才用于查找值。所以,mapping是没有长度和设置key和value的概念。mapping只允许静态变量或是内部方法中的存储空间引用类型。

复制代码
pragma solidity ^0.4.0;

contract MappingExample {
    mapping(address => uint) public balances;

    function update(uint newBalance) {
        balances[msg.sender] = newBalance;
    }
}

contract MappingUser {
    function f() returns (uint) {
        return MappingExample(<address>).balances(this);
    }
}

 

数据位置

所有的复杂类型,即 数组 和 结构 类型,都有一个额外属性,“数据位置”,说明数据是保存在 内存memory 中还是 存储storage 中。 根据上下文不同,大多数时候数据有默认的位置,但也可以通过在类型名后增加关键字 storage 或 memory 进行修改。 函数参数(包括返回的参数)的数据位置默认是 memory, 局部变量的数据位置默认是 storage,状态变量的数据位置强制是 storage (这是显而易见的)。

也存在第三种数据位置, calldata ,这是一块只读的,且不会永久存储的位置,用来存储函数参数。 外部函数的参数(非返回参数)的数据位置被强制指定为 calldata ,效果跟 memory 差不多。

数据位置的指定非常重要,因为它们影响着赋值行为: 在 存储storage 和 内存memory 之间两两赋值,或者 存储storage 向状态变量(甚至是从其它状态变量)赋值都会创建一份独立的拷贝。 然而状态变量向局部变量赋值时仅仅传递一个引用,而且这个引用总是指向状态变量,因此后者改变的同时前者也会发生改变。 另一方面,从一个 内存memory 存储的引用类型向另一个 内存memory 存储的引用类型赋值并不会创建拷贝。

pragma solidity ^0.4.0;

contract C {
    uint[] x; // x 的数据存储位置是 storage

    // memoryArray 的数据存储位置是 memory
    function f(uint[] memoryArray) public {
        x = memoryArray; // 将整个数组拷贝到 storage 中,可行
        var y = x;  // 分配一个指针(其中 y 的数据存储位置是 storage),可行
        y[7]; // 返回第 8 个元素,可行
        y.length = 2; // 通过 y 修改 x,可行
        delete x; // 清除数组,同时修改 y,可行
        // 下面的就不可行了;需要在 storage 中创建新的未命名的临时数组, /
        // 但 storage 是“静态”分配的:
        // y = memoryArray;
        // 下面这一行也不可行,因为这会“重置”指针,
        // 但并没有可以让它指向的合适的存储位置。
        // delete y;

        g(x); // 调用 g 函数,同时移交对 x 的引用
        h(x); // 调用 h 函数,同时在 memory 中创建一个独立的临时拷贝
    }

    function g(uint[] storage storageArray) internal {}
    function h(uint[] memoryArray) public {}
}

总结

强制指定的数据位置:
  • 外部函数的参数(不包括返回参数): calldata
  • 状态变量: storage
默认数据位置:
  • 函数参数(包括返回参数): memory
  • 所有其它局部变量: storage

标签:函数,storage,solidity,数据类型,mapping,uint,数组,memory,类型
From: https://www.cnblogs.com/shyroke/p/16754252.html

相关文章

  • 函数
    函数概念及常见函数1.函数概念定义如果对于每个数\(x\inD\),变量\(y\)按照一定的法则总有一个确定的\(y\)和它对应,则称\(x\)是\(y\)的函数,记为\(y......
  • 多元函数的极限、连续与微分1
    $1.求极限:(1)\lim_{(x,y)\to(0,0)}(1+x^2+y^2)^{\frac{xy}{x^2+y^2}}(2)\lim_{x\to\infty,y\to\infty}\frac{x+y}{x^2-xy+y^2}$\((1)\)\[\lim_{(x,y)\to(0,0)}......
  • selenium 函数汇总
    目录截图滚动条相关操作判断状态获取网页相关数据浏览器操作元素操作截图截某个元素的图ele=driver.find_element(By.XPATH,"//div[@class='alertalert-successale......
  • __builtin_expect函数
    一、背景在很多源码如Linux内核、Glib等,我们都能看到likely()和unlikely()这两个宏,通常定义如下#definelikely(x)__builtin_expect(!!(x),1)#defineunlikely(x)......
  • 关于python函数中带*星号参数-收集参数的使用说明
    在python中,定时函数时,一般就得确定函数的参数的个数当然函数可以没有参数,也可以指定明确的形式参数的个数,那样在调用这个函数时,实参的个数就需要与形参个数一致defPrin......
  • 函数传参的细节
         ......
  • 数据类型
    publicclassDemo{publicstaticvoidmain(String[]args){inti=10;inti2=010;inti3=0x1A;//16进制0-9A-Finti4=0b0010;......
  • 函数的递归调用
    介绍:一个函数在函数体内又调用了本身,称之为递归调用例子:  ①当在函数main内调用test(4)时,执行判断if,由于4>2,执行test(n-1),此时n=4,则传值为test(3)②继续执行判断if......
  • 网络字节序与主机字节序的转换函数实践
    字节序(1)即字节在电脑中存放时的序列与输入(输出)时的序列是先到的在前还是后到的在前。字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序(2)分类......
  • js 函数
    延迟执行functiondebounce(wait){vartimer=null;returnfunction(fn){if(timer!==null){clearTimeout(timer);}timer=setTimeo......