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

在这里插入图片描述

引用类型

引用类型的值可以通过多个不同的名称进行修改。这与值类型形成对比,在值类型中,每当使用一个值类型的变量时,都会获得一个独立的副本。因此,引用类型比值类型需要更谨慎地处理。

目前,引用类型包括结构体(structs)、数组(arrays)和映射(mappings)。如果使用引用类型,必须明确提供数据存储的位置:memory(其生命周期仅限于外部函数调用期间)、storage(存储状态变量的位置,其生命周期与合约的生命周期一致)或 calldata(一个特殊的数据存储区域,其中包含函数参数)。

如果赋值或类型转换导致数据存储位置发生变化,则会自动触发复制操作,而在同一数据存储位置内部进行赋值时,仅在某些情况下会触发复制(对于 storage 类型)。

数据存储位置

每个引用类型都有一个额外的注释,即“数据存储位置”,用于指明其存储位置。数据存储位置包括 memory、storage 和 calldata。calldata 是一个不可修改、不可持久化的区域,其中存储了函数参数,其行为大多数情况下类似于 memory。

在函数体中声明或作为返回参数的 calldata 位置的数组和结构体必须在使用或返回之前进行赋值。在某些使用非平凡控制流的情况下,编译器可能无法正确检测初始化。在这些情况下,一个常见的解决方法是先将受影响的变量赋值给自身,然后再进行正确的初始化。

分配行为

数据存储位置不仅与数据的持久性相关,还会影响赋值的语义:

  • 在 storage 和 memory(或 calldata)之间的赋值总是会创建一个独立的副本。

  • 在 memory 之间的赋值仅创建引用。这意味着对一个 memory 变量的修改会影响所有引用同一数据的 memory 变量。

  • 从 storage 赋值给本地 storage 变量时,也只是赋值引用。

  • 其他所有对 storage 的赋值都会进行复制。例如,对状态变量的赋值,或对 storage 结构体类型的本地变量的成员赋值,即使本地变量本身只是一个引用。

举个例子:

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

contract C {
    // x 的数据存储位置是 storage。
    // 这是唯一可以省略数据存储位置的地方。
    uint[] x;

    // memoryArray 的数据存储位置是 memory。
    function f(uint[] memory memoryArray) public {
        x = memoryArray; // 可以执行,会复制整个数组到 storage
        uint[] storage y = x; // 可以执行,赋值的是指针,y 的数据存储位置是 storage
        y[7]; // 合法,返回第 8 个元素
        y.pop(); // 合法,通过 y 修改 x
        delete x; // 合法,清空数组,同时影响 y

        // 以下操作无法执行,因为它需要在 storage 中创建一个新的临时/匿名数组,
        // 但 storage 是静态分配的:
        // y = memoryArray;

        // 同样,“delete y” 也是不合法的,因为对指向 storage 对象的本地变量的赋值
        // 只能来自已有的 storage 对象。
        // 它会“重置”指针,但没有合理的位置可以指向。
        // 更多细节请参考“delete” 运算符的文档。
        // delete y;

        g(x); // 调用 g,传递 x 的引用
        h(x); // 调用 h,创建一个独立的临时副本存储在 memory
    }

    function g(uint[] storage) internal pure {}
    function h(uint[] memory) public pure {}
}

数组

数组可以是编译时固定大小的,也可以是动态大小的。

固定大小为 k,元素类型为 T 的数组写作 T[k],而动态大小的数组写作 T[]。

例如,一个包含 5 个 uint 动态数组的数组写作 uint[][5]。该表示法与某些其他语言相反。在 Solidity 中,X[3] 始终是一个包含 3 个 X 类型元素的数组,即使 X 本身是一个数组。而在 C 语言等其他语言中,这种情况可能不同。

索引从 0 开始,访问顺序与声明顺序相反。

例如,如果有一个变量 uint[][5] memory x,要访问第三个

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋说

感谢打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值