【区块链安全 | 第二十一篇】类型之基础类型之间的转换

在这里插入图片描述

基础类型之间的转换

隐式转换

隐式类型转换是在某些场景下由编译器自动执行的操作,例如赋值、函数参数传递或应用运算符时。通常,只有在值类型之间转换不会导致语义歧义或信息丢失的情况下,才会发生隐式转换。

例如,uint8 可以转换为 uint16,int128 可以转换为 int256,但 int8 不能转换为 uint256,因为 uint256 无法表示负数(如 -1)。

当对不同类型的操作数应用运算符时,编译器会尝试将其中一个操作数隐式转换为另一个操作数的类型(赋值操作时也是如此)。这意味着运算将始终在某一方的类型下执行。

关于哪些隐式转换是允许的,请参阅相关类型的具体章节。

例如,y 和 z 是两个加法操作数,它们的类型不同,但由于 uint8 可以隐式转换为 uint16,而 uint16 不能隐式转换为 uint8,所以在运算执行前,y 会被转换为 z 的类型,即 uint16。表达式 y + z 的结果类型为 uint16,然后由于赋值给 uint32 类型的变量,因此结果又会被隐式转换为 uint32。

uint8 y;
uint16 z;
uint32 x = y + z;

显式转换

当编译器不允许隐式转换时,若你确信转换是安全的,可以使用显式类型转换。然而,这种操作可能会引发非预期行为,并绕过编译器的一些类型安全检查,因此务必确保转换结果符合预期,并进行充分测试。

以下是一个将负数 int 转换为 uint 的示例:

int y = -3;
uint x = uint(y);

在上述代码中,x 的值将为 0xffff…fd(共 64 个十六进制字符),这是 -3 在 256 位二进制补码表示中的形式。

若将整数显式转换为更小的类型,高位将会被截断:

uint32 a = 0x12345678;
uint16 b = uint16(a); // b 现在将是 0x5678

若将整数显式转换为更大的类型,转换结果将在高位进行零填充,即保留原始整数的数值:

uint16 a = 0x1234;
uint32 b = uint32(a); // b 现在将是 0x00001234
assert(a == b);

固定大小字节类型在转换时行为略有不同。它们被视为字节序列,转换为较小类型时会进行截断:

bytes2 a = 0x1234;
bytes1 b = bytes1(a); // b 将是 0x12

转换为更大的固定字节类型时,将在末尾填充零。转换前后访问索引处的字节值相同(前提是索引仍在有效范围内):

bytes2 a = 0x1234;
bytes4 b = bytes4(a); // b 现在将是 0x12340000
assert(a[0] == b[0]);
assert(a[1] == b[1]);

由于整数与固定大小字节数组在截断和填充行为上的差异,只有在它们的大小相同的情况下,才允许二者之间进行显式转换。若要在不同大小之间进行转换,需使用中间转换步骤以明确指定截断或填充规则:

bytes2 a = 0x1234;
uint32 b = uint16(a); // b 将是 0x00001234
uint32 c = uint32(bytes4(a)); // c 将是 0x12340000
uint8 d = uint8(uint16(a)); // d 将是 0x34
uint8 e = uint8(bytes1(a)); // e 将是 0x12

动态字节数组(bytes)和 bytes calldata 切片可以显式转换为固定字节类型(bytes1 至 bytes32)。若数组长度超过目标固定字节类型的大小,将会进行截断;若长度不足,则会在末尾填充零。

// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.5;

contract C {
    bytes s = "abcdefgh";
    function f(bytes calldata c, bytes memory m) public view returns (bytes16, bytes3) {
        require(c.length == 16, "");
        bytes16 b = bytes16(m);  // 如果 m 的长度大于 16,将发生截断
        b = bytes16(s);  // 右侧填充零,结果为 "abcdefgh\0\0\0\0\0\0\0\0"
        bytes3 b1 = bytes3(s); // 截断,b1 等于 "abc"
        b = bytes16(c[:8]);  // 也用零填充
        return (b, b1);
    }
}

关键点:
1.显式转换可以绕过编译器的类型检查,需谨慎使用。
2.转换为更小类型时将截断高位;转换为更大类型时将高位填充零。
3.固定大小字节数组在转换过程中会根据目标类型大小截断或填充。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

秋说

感谢打赏

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

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

打赏作者

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

抵扣说明:

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

余额充值