当前位置: 首页 > news >正文

【区块链安全 | 第十八篇】类型之引用类型(二)

文章目录

  • 引用类型
    • 数组切片
    • 结构体

在这里插入图片描述

引用类型

数组切片

数组切片是对数组中连续部分的一个视图。它的语法为 x[start:end],其中 startend 是表达式,结果类型为 uint256(或者可以隐式转换为 uint256)。切片的第一个元素是 x[start],最后一个元素是 x[end - 1]

如果 start 大于 end,或者 end 大于数组的长度,则会抛出异常。

startend 都是可选的:start 默认为 0,end 默认为数组的长度。

数组切片没有成员。它们可以隐式地转换为其底层类型的数组,并支持索引访问。索引访问相对于切片的起始位置,而不是底层数组的绝对位置。

数组切片没有类型名称,这意味着没有变量可以将数组切片作为类型,它们仅存在于中间表达式中。

注意
目前,数组切片仅在 calldata 类型的数组上可用。数组切片在 ABI 解码函数参数中的辅助数据时非常有用:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.8.5 <0.9.0;

contract Proxy {
    /// @dev 代理管理的客户端合约地址
    address client;

    constructor(address client_) {
        client = client_;
    }

    /// 在对地址参数进行基本验证后,转发调用到客户端实现的 "setOwner(address)"。
    function forward(bytes calldata payload) external {
        bytes4 sig = bytes4(payload[:4]);
        // 由于截断行为,bytes4(payload) 的效果是一样的。
        // bytes4 sig = bytes4(payload);
        if (sig == bytes4(keccak256("setOwner(address)"))) {
            address owner = abi.decode(payload[4:], (address));
            require(owner != address(0), "Address of owner cannot be zero.");
        }
        (bool status,) = client.delegatecall(payload);
        require(status, "Forwarded call failed.");
    }
}

结构体

Solidity 提供了一种定义新类型的方式,即结构体(struct),如下例所示:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.9.0;

// 定义一个具有两个字段的新类型。
// 在合约外部声明结构体允许它被多个合约共享。
// 这里其实不需要这样做。
struct Funder {
    address addr;
    uint amount;
}

contract CrowdFunding {
    // 结构体也可以在合约内部定义,这样它只在合约内部以及派生合约中可见。
    struct Campaign {
        address payable beneficiary;
        uint fundingGoal;
        uint numFunders;
        uint amount;
        mapping(uint => Funder) funders;
    }

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

    function newCampaign(address payable beneficiary, uint goal) public returns (uint campaignID) {
        campaignID = numCampaigns++; // campaignID 是返回的变量
        // 我们不能使用 "campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0)"
        // 因为右侧会创建一个内存结构体 "Campaign",其中包含一个映射。
        Campaign storage c = campaigns[campaignID];
        c.beneficiary = beneficiary;
        c.fundingGoal = goal;
    }

    function contribute(uint campaignID) public payable {
        Campaign storage c = campaigns[campaignID];
        // 创建一个新的临时内存结构体,并用给定的值初始化,
        // 然后将其复制到存储中。
        // 注意,你也可以使用 Funder(msg.sender, msg.value) 来初始化。
        c.funders[c.numFunders++] = Funder({addr: msg.sender, amount: msg.value});
        c.amount += msg.value;
    }

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

该合约并没有提供一个完整的众筹合约功能,但它包含了理解结构体所需的基本概念。结构体类型可以在映射和数组中使用,结构体本身也可以包含映射和数组。

需要注意的是,结构体不能包含其自身类型的成员,尽管结构体可以作为映射成员的值类型,或者可以包含其类型的动态大小数组。这一限制是必要的,因为结构体的大小必须是有限的。

在所有函数中,结构体类型都被分配给了一个数据位置为 storage 的局部变量。这并不会复制结构体,而只是存储一个引用,以便对局部变量成员的赋值实际上会写入状态。

当然,你也可以直接访问结构体的成员,而不必将其分配给局部变量,就像 campaigns[campaignID].amount = 0 这样。

注意
在 Solidity 0.7.0 及之前的版本,允许内存结构体包含存储类型的成员(如映射),并且像上例中的 campaigns[campaignID] = Campaign(beneficiary, goal, 0, 0) 这样的赋值会正常执行,但会默默跳过这些成员。

相关文章:

  • 开源深度学习框架PyTorch
  • 为什么要指针压缩,为什么能指针压缩?原理是什么?
  • 01小游戏
  • 3月31号
  • lib-zo,C语言另一个协程库,激活文件IO操作协程化
  • http知识点
  • 2025年浙江省中等职业学校职业能力大赛(学生技术技能类)“移动应用与开发”赛项技术文件
  • FFTW库在vs2022下编译lib库及在QT6.8中调用
  • LeetCode hot 100—二叉搜索树中第K小的元素
  • 【VUE2】综合练习——智慧商城
  • visio导出pdf公式变形
  • Embedding原理
  • zk基础—1.一致性原理和算法一
  • 《算法:递归+记忆化搜索》
  • 【计算机视觉】OpenCV实战项目- 抖音动态小表情
  • ESP32移植Openharmony外设篇(11) mfrc522射频读卡器
  • 数据处理与机器学习入门
  • MyBatisPlus不等于如何使用
  • qml 中的anchors
  • dfs复习
  • 做seo必须有自己网站吗/百度提问在线回答问题
  • 做网站开发的想接私活/在线培训管理系统
  • dw网站引导页怎么做/搜索引擎优化方法总结
  • 塔式服务器主机建网站/市场营销方案
  • 计算机网站建设/网站优化公司哪个好
  • 彩票娱乐网站建设开发/太原竞价托管公司推荐