【区块链安全 | 第十四篇】类型之值类型(一)
文章目录
- 值类型
- 布尔值
- 整数
- 运算符
- 取模运算
- 指数运算
- 定点数
- 地址(Address)
- 类型转换
- 地址的成员
- balance 和 transfer
- send
- call,delegatecall 和 staticcall
- code 和 codehash
- 合约类型(Contract Types)
- 固定大小字节数组(Fixed-size byte arrays)
- 地址字面量(Address Literals)

Solidity 是一种静态类型语言,这意味着每个变量(无论是状态变量还是局部变量)都需要指定其类型。Solidity 提供了几种基本类型,这些类型可以组合形成复杂类型。
此外,不同类型可以在包含运算符的表达式中相互作用,并且具有优先级的区分。
Solidity 没有“未定义”或“空”值的概念,但新声明的变量总是具有默认值,该默认值取决于其类型。为了处理任何意外的值,应该使用 revert 函数回滚整个交易,或者返回一个带有布尔值的元组,其中第二个 bool 值表示操作是否成功。
值类型
值类型的变量始终按值传递,即在作为函数参数或赋值时总是被复制。
与引用类型不同,值类型的声明不指定数据位置,因为它们足够小,可以存储在栈中。唯一的例外是状态变量,状态变量默认存储在存储中,但也可以标记为 transient、constant 或 immutable。
布尔值
bool:值是 true 或 false。
整数
int / uint:各种大小的有符号和无符号整数。
关键字 uint8 到 uint256(以 8 为步长,表示 8 位到 256 位的无符号整数),以及 int8 到 int256。
uint 和 int 分别是 uint256 和 int256 的别名。
运算符
比较运算符:<=,<,==,!=,>=,>(结果为 bool)
位运算符:&,|,^(按位异或),~(按位取反)
移位运算符:<<(左移),>>(右移)
算术运算符:+,-,一元 -(仅适用于有符号整数),*,/,%(取模),**(指数运算)
对于整数类型 X,可以使用 type(X).min 和 type(X).max 来访问该类型可表示的最小值和最大值。
运算符 || 和 && 遵循短路规则。这意味着在表达式 f(x) || g(y) 中,如果 f(x) 计算结果为 true,则 g(y) 将不会被计算。
注意
Solidity 中的整数受到特定范围的限制。例如,对于 uint32,其范围为 0 到 2**32 - 1。
在 Solidity 中,整数运算有两种模式:“溢出”模式(wrapping/unchecked mode) 和 “检查”模式(checked mode)。
默认情况下,运算始终处于 “检查”模式,这意味着如果运算结果超出了类型的值范围,则调用会因失败的断言而回滚。
可以使用 unchecked { ... } 切换到 “溢出”模式,但应谨慎使用。
取模运算
% 的结果是操作数 a 除以操作数 n 后的余数 r,其中 q = int(a / n),并且 r = a - (n * q)。
这意味着取模运算的结果与其左操作数的符号相同(或为零),并且对于负数 a,a % n == -(-a % n) 恒成立:
int256(5) % int256(2) == int256(1)int256(5) % int256(-2) == int256(1)int256(-5) % int256(2) == int256(-1)int256(-5) % int256(-2) == int256(-1)
注意:使用 0 作为取模运算的除数会导致 Panic 错误。此检查无法通过 unchecked { ... } 关闭。
指数运算
指数运算 ** 仅适用于无符号类型作为指数(幂)。指数运算的结果类型始终与底数的类型相同。请确保底数足够大以容纳结果,并预防潜在的断言失败或溢出行为。
注意:在 检查模式(checked mode)下,对于较小的底数,指数运算仅使用相对廉价的 EXP 操作码。
例如,在计算 x**3 时,使用 x*x*x 可能更便宜。因此,建议进行 Gas 成本测试 并使用优化器。此外,EVM 规定 0**0 的结果为 1。
定点数
警告:定点数在 Solidity 中尚不完全支持。它们可以被声明,但不能进行赋值操作。
fixed / ufixed:带符号和无符号定点数,具有不同的大小。关键字 ufixedMxN 和 fixedMxN,其中 M 代表类型所占用的位数,N 代表可用的小数位数。M 必须是 8 的倍数,范围从 8 到 256 位。N 必须在 0 到 80 之间(包含 0 和 80)。ufixed 和 fixed 是 ufixed128x18 和 fixed128x18 的别名。
操作符
-
比较操作符:
<=,<,==,!=,>=,>(结果为布尔值) -
算术操作符:
+,-,一元-(仅对带符号数),*,/,%(取模)
注意:浮动点数(许多语言中的 float 和 double,更精确地说是 IEEE 754 数字)和定点数的主要区别在于,浮动点数用于表示整数和小数部分的位数是灵活的,而定点数则严格定义了每部分所占的位数。通常,在浮动点数中,几乎整个空间用于表示数字,而只有少数位数用于定义小数点的位置。
地址(Address)
地址类型有两种主要相似的变体:
address:持有一个 20 字节的值(以太坊地址的大小)。address payable:与address相同,但具有额外的transfer和send成员。
这种区分的想法是,address payable 是一个可以接收以太币的地址,而普通的 address 不能接收以太币,例如,它可能是一个不支持接收以太币的智能合约。
类型转换
1.允许从 address payable 到 address 的隐式转换,而从 address 到 address payable 必须通过显式转换 payable(<address>)。
2.允许显式转换到 address 类型并返回 uint160、整数字面量、bytes20 和合约类型。
3.只有 address 类型和合约类型的表达式可以通过显式转换 payable(...) 转换为 address payable。对于合约类型,只有在合约可以接收以太币的情况下(即合约有 receive 或 payable 回退函数)才能进行这种转换。注意,payable(0) 是有效的,且是这一规则的例外。
注意
如果我们需要一个 address 类型的变量,并计划向其发送以太币,那么应将其声明为 address payable 以使该要求更加明显。此外,尽量在最早阶段做出这种区分或转换。
从 0.5.0 版本开始,address 和 address payable 之间的区分被引入。自那时起,合约不能隐式转换为 address 类型,但如果它们有 receive 或 payable 回退函数,仍然可以显式地转换为 address 或 address payable。
地址的成员
balance 和 transfer
可以使用 balance 属性查询地址的余额,并使用 transfer 函数将以太币(以 wei 为单位)发送到可支付的地址:
address payable x = payable(0x123);
address myAddress = address(this);
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);
transfer 函数在当前合约的余额不足或接收方帐户拒绝接收以太币时失败,并会回滚操作。
注意
如果 x 是一个合约地址,它的代码(更具体地说:其 Receive Ether 函数如果存在,或其他回退函数如果存在)将在 transfer 调用时一起执行,这是 EVM 的特性,无法阻止。如果该执行耗尽了 gas 或以任何方式失败,Ether 转账将回滚,当前合约将停止并抛出异常。
send
send 是 transfer 的低级对等函数。如果执行失败,当前合约不会停止并抛出异常,而是返回 false。
使用 send 时存在一些危险:如果调用堆栈深度达到 1024(调用者可以强制此情况),或者接收方耗尽 gas,则转账将失败。因此,为了安全地转账以太币,始终检查 send 的返回值,使用 transfer 或更好的方式是使用一个模式,其中接收方自己提取以太币。
call,delegatecall 和 staticcall
为了与不符合 ABI 的合约交互,或为了更直接地控制编码,可以使用 call、delegatecall 和 staticcall 函数。它们都接受一个字节内存参数,并返回成功条件(布尔值)和返回的数据(字节内存)。abi.encode、abi.encodePacked、abi.encodeWithSelector 和 abi.encodeWithSignature 可以用来编码结构化数据。
示例:
bytes memory payload = abi.encodeWithSignature("register(string)", "MyName");
(bool success, bytes memory returnData) = address(nameReg).call(payload);
require(success);
注意,这些都是低级函数,应该小心使用。特别是,任何未知的合约可能是恶意的,如果调用它,你将把控制权交给该合约,而该合约可能会再次调用你的合约,因此当调用返回时,需要做好准备处理可能会更改的状态变量。与其他合约交互的常规方式是调用合约对象上的函数(例如 x.f())。
可以使用 gas 修饰符调整提供的 gas:
address(nameReg).call{gas: 1000000}(abi.encodeWithSignature("register(string)", "MyName"));
类似地,也可以控制提供的 Ether 数量:
address(nameReg).call{value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));
最后,这些修饰符可以结合使用,它们的顺序无关紧要:
address(nameReg).call{gas: 1000000, value: 1 ether}(abi.encodeWithSignature("register(string)", "MyName"));
类似地,可以使用 delegatecall 函数,不同之处在于,只有给定地址的代码会被使用,所有其他方面(存储、余额等)都来自当前合约。delegatecall 的目的是使用存储在另一个合约中的库代码。用户必须确保两个合约的存储布局适合使用 delegatecall。
code 和 codehash
你可以查询任何智能合约的已部署代码。使用 .code 获取 EVM 字节码作为字节内存(可能为空)。使用 .codehash 获取该代码的 Keccak-256 哈希(作为 bytes32)。注意,addr.codehash 比使用 keccak256(addr.code) 更便宜。
如果与 addr 相关联的帐户为空或不存在(即它没有代码、零余额和零 nonce,如 EIP-161 所定义),则 addr.codehash 的输出可能为 0。如果该帐户没有代码,但有非零余额或 nonce,则 addr.codehash 将输出空数据的 Keccak-256 哈希(即 keccak256(""),其结果为 c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470)。
注意,所有合约都可以转换为 address 类型,因此可以使用 address(this).balance 查询当前合约的余额。
合约类型(Contract Types)
每个合约都定义了自己的类型。你可以将合约隐式转换为它继承的合约类型。合约类型可以显式地转换为 address 类型,反之亦然。
显式转换到 address payable 类型仅在合约类型有 receive 或 payable 回退函数时才可能。转换仍然通过 address(x) 进行。如果合约类型没有 receive 或 payable 回退函数,可以使用 payable(address(x)) 进行转换。
注意
1.如果你声明一个合约类型的局部变量(例如 MyContract c),你可以在该合约上调用函数。需要注意的是,必须从与之相同的合约类型赋值给该变量。
2.你也可以实例化合约(这意味着它们是新创建的)。你可以在“通过 new 创建合约”部分找到更多的细节。
3.合约的数据显示方式与 address 类型相同,并且这种类型也用于 ABI 中。
4.合约不支持任何操作符。
5.合约类型的成员是该合约的外部函数,包括任何标记为 public 的状态变量。
6.对于合约 C,你可以使用 type(C) 来访问有关该合约的类型信息。
固定大小字节数组(Fixed-size byte arrays)
值类型 bytes1, bytes2, bytes3, …, bytes32 用于存储从 1 到 32 字节的字节序列。
操作符:
- 比较操作符:
<=,<,==,!=,>=,>(返回布尔值) - 位操作符:
&,|,^(按位异或),~(按位取反) - 移位操作符:
<<(左移),>>(右移) - 索引访问:如果
x是类型bytesI,则x[k](0 <= k < I)返回第k个字节(只读)。
移位操作符与无符号整数类型作为右操作数一起工作(但返回左操作数的类型),表示要移位的位数。使用有符号类型进行移位会导致编译错误。
成员.length 可以返回字节数组的固定长度(只读)。
注意
类型 bytes1[] 是字节的数组,但由于填充规则,对于每个元素,它浪费 31 字节的空间(在存储中除外)。最好使用 bytes 类型。
地址字面量(Address Literals)
地址字面量是通过地址校验和测试的十六进制字面量,例如 0xdCad3a6d3569DF655070DEd06cb7A1b2Ccd1D3AF 是 address 类型。长度在 39 到 41 位之间且未通过校验和测试的十六进制字面量会产生错误。
我们可以通过在前面(对于整数类型)或后面(对于 bytesNN 类型)加零来去除错误。
相关文章:
【区块链安全 | 第十四篇】类型之值类型(一)
文章目录 值类型布尔值整数运算符取模运算指数运算 定点数地址(Address)类型转换地址的成员balance 和 transfersendcall,delegatecall 和 staticcallcode 和 codehash 合约类型(Contract Types)固定大小字节数组&…...
音视频入门基础:MPEG2-TS专题(25)——通过FFmpeg命令使用UDP发送TS流
一、通过FFmpeg命令使用UDP发送TS流 通过以下FFmpeg命令可以将一个mp4文件转换为ts封装,并基于UDP发送(推流): ffmpeg.exe -re -i input.mp4 -vcodec copy -acodec copy -f mpegts udp://127.0.0.1:1234 其中: “in…...
Error in torch with streamlit
报错信息: This is the error which is a conflict between torch and streamlit: Examining the path of torch.classes raised: Tried to instantiate class path.path’, but it does not exist! Ensure that it is registered via torch::class Steps to reproduce: py…...
网络基础知识介绍
目录 一、计算机网络背景与发展 1.1 计算机网络的背景 编辑1.2 计算机网络的发展历程 二、网络协议 2.1 认识网络协议 2.3 协议分层 2.4 OSI七层模型 2.5 TCP/IP 五层(或四层)模型 三、网络传输基本流程 3.1 网络传输流…...
MIPS-32架构(寄存器堆,指令系统,运算器)
文章目录 0 Preview:寄存器32通用0 $zero1 $at2—3 \$v0-$v14—7 \$a0-$a38—15 \$t0-$t716—23 \$s0-$s724—25 \$t8-$t926—27 \$k0-$k128 $gp29 $sp30 $fp 指令系统运算存储器 0 Preview: MIPS架构有32位版本和64位版本,本文介绍32位版本 寄存器 正如笔者曾说…...
zip和tar.gz
本文来源: 在压缩文件格式选择上,zip和tar.gz有本质差异,主要体现在以下五个方面: 一、压缩机制不同 tar.gz是"两步走"方案:先用tar工具将多个文件/目录打包为单个未压缩的归档文件(保留权限…...
【什么是机器学习——多项式逼近】
什么是机器学习——多项式逼近 机器学习可以分成三大类别,监督学习、非监督学习、强化学习。三大类别背后的数学原理不同。监督学习使用了数学分析中的函数逼近方法和概率统计中的极大似然方法;非监督学习使用聚类和EM算法;强化学习使用马尔可夫决策过程的想法。 机器学习的…...
C++中的搜索算法实现
C中的搜索算法实现 在编程中,搜索算法是解决各种问题的基础工具之一。C作为一种功能强大的编程语言,提供了多种实现搜索算法的方式。本文将详细介绍两种常见的搜索算法:线性搜索和二分搜索,并通过代码示例展示它们的实现。 一、…...
(二十三)Dart 中的 Mixins 使用教程
Dart 中的 Mixins 使用教程 Mixins 简介 Mixins 是 Dart 中一种强大的特性,中文意思是“混入”,它允许在类中混入其他功能,从而实现类似多继承的功能。与传统的继承不同,Mixins 提供了一种更加灵活的方式来组合类的功能…...
《午夜地铁的幽灵AP》
点击下面图片带您领略全新的嵌入式学习路线 🔥爆款热榜 88万阅读 1.6万收藏 文章目录 **第一章:末班车的二进制月光****第二章:ESP32的赛博墓志铭****第三章:都市传说与CRC校验****第四章:数字孪生的献祭仪式****终章…...
创作领域“<em >彩</em><em>票</em><em>导</em><em>师</em><em>带</em><em>玩</em><em>群
天光揉碎最后一块夜斑,露珠压弯草叶的脆响惊醒了沉睡的巷子。青灰雾霭中,老墙上的爬山虎在打哈欠,卷曲的藤须滴落隔夜的月光。sFsTU...
Spring Cloud Gateway中GatewayFilter Factories(网关过滤工厂)的详细介绍
文章目录 1、网关过滤工厂介绍2、 GatewayFilter 过滤器的基本配置3、 Spring Cloud Gateway 内置 GatewayFilter Factories3.1、AddRequestHeader GatewayFilter3.2、AddResponseHeader GatewayFilter3.3、AddRequestParameter GatewayFilter3.4、RewritePath GatewayFilter3.…...
微服务架构:构建可持续演进的微服务架构的原则与实践指南
引言:微服务的价值锚点 某物流公司微服务化后,订单履约周期从2小时缩短至15分钟,但技术债务却以每年200%的速度增长。这个案例揭示了一个关键认知:微服务架构的成败不在于技术实现,而在于是否建立有效的演进机制。…...
C++的四种类型转换
文章目录 const_cast:去掉常量类型的类型转换static_cast:提供编译器认为安全的类型转换(在编译阶段完成类型转换)reinterpret:类似c风格的强制类型转化dynamic_cast:主要用在继承结构里,可以支持RTTI类型识别的上下转换dynamic_cast<>…...
Python Cookbook-4.15 字典的一键多值
任务 需要一个字典,能够将每个键映射到多个值上。 解决方案 正常情况下,字典是一对一映射的,但要实现一对多映射也不难,换句话说,即一个键对应多个值。你有两个可选方案,但具体要看你怎么看待键的多个对…...
《Python实战进阶》No37: 强化学习入门加餐版3 之 Q-Learning算法可视化升级
连续第4篇文章写Q-Learning算法及可视化 Q-Learning强化学习算法在迷宫寻路中的应用 引言 强化学习是机器学习的一个重要分支,其核心理念是通过与环境的交互来学习最优策略。在上三篇文章中,《Python实战进阶》No37: 强化学习入门:Q-Learn…...
1.两数之和(Java)
1. 题目描述 LeetCode 1. 两数之和(Two Sum) 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回它们的索引。 示例 1: 输入:nums [2,7,11,15], target 9 …...
漏洞挖掘---灵当CRM客户管理系统getOrderList SQL注入漏洞
一、灵当CRM 灵当CRM是上海灵当信息科技有限公司旗下产品,适用于中小型企业。它功能丰富,涵盖销售、服务、财务等管理功能,具有性价比高、简洁易用、可定制、部署灵活等特点,能助力企业提升经营效益和客户满意度。 二、FOFA-Sear…...
Java高频面试之集合-20
hello啊,各位观众姥爷们!!!本baby今天来报道了!哈哈哈哈哈嗝🐶 面试官:讲讲 HashSet 的底层实现? HashSet 是 Java 集合框架中用于存储唯一元素的高效数据结构,其底层实…...
sort命令:排序
sort:默认首位排序 参数: -n:按整个数字排序 -r:降序 -u:去重 [rootrobin ~]# sort -n aa.txt #按数字排序(正序) [rootrobin ~]# sort -nr aa.txt #降序 [rootrobin ~]# sort -…...
Javaweb后端 AOP快速入门 AOP核心概念 AOP执行流程
AOP是对特定方法编程,把共用都用的方法提取出来,统一维护 AOP基础 AOP快速入门 对原始方法无影响 AOP核心概念 连接点,是原始方法,被控制范围内的原始方法 通知,AOP类里面写的公共的方法 切入点,实际被AO…...
deepseek ai 输入法
一、简介 使用java开发一个安卓输入法接入deepseek实现ai聊天,代码已开源。 二、视频演示 deepseek输入法_哔哩哔哩_bilibili 三、开源地址 https://github.com/deepseek/inputmethed 四、技术细节 CustomInputMethodService.java 输入法服务类 MainActivity.…...
Rust 所有权与引用
目录 Rust 所有权原则变量所有权变量作用范围深拷贝 Rust 的引用示例可变引用不可变引用可变引用和不可变引用不能同时存在悬垂引用 Rust 所有权原则 Rust 中每一个值都被一个变量所拥有,该变量被称为值的所有者一个值同时只能被一个变量所拥有,或者说一…...
探究 CSS 如何在HTML中工作
2025/3/28 向全栈工程师迈进! 一、CSS的作用 简单一句话——美化网页 <p>Lets use:<span>Cascading</span><span>Style</span><span>Sheets</span> </p> 对于如上代码来说,其显示效果如下࿱…...
Verilog中X态的危险:仿真漏掉的bug
由于Verilog中X态的微妙语义,RTL仿真可能PASS,而网表仿真却会fail。 目前进行的网表仿真越来越少,这个问题尤其严重,主要是网表仿真比RTL仿真慢得多,因此对整个回归测试而言成本效益不高。 上面的例子中,用…...
使用 uv 管理 Python 项目
介绍 首先, uv 工具是使用 rust 开发出来的, 速度要比传统的 pip, pipx 等一众包管理工具要快不少. 另外, 除了包管理之外, uv 还提供了脚手架的功能, 使用体验和前端开发使用过的 vue-cli 很相似, 可以帮助我们自动初始化项目, 创建好一个空的包含必要文件结构的文件夹. 此外…...
【操作系统】软中断vs硬中断
在操作系统中,中断(Interrupt) 是 CPU 响应外部事件的重要机制,分为 硬中断(Hardware Interrupt) 和 软中断(Software Interrupt)。它们的核心区别在于 触发方式 和 处理机制。 1. 硬…...
《C++11:通过thread类编写C++多线程程序》
关于多线程的概念与理解,可以先了解Linux下的底层线程。当对底层线程有了一定程度理解以后,再学习语言级别的多线程编程就轻而易举了。 【Linux】多线程 -> 从线程概念到线程控制 【Linux】多线程 -> 线程互斥与死锁 语言级别的…...
19-dfs-排列数字(基础)
题目 来源 842. 排列数字 - AcWing题库 思路 由于相对简单,是dfs的模板题,具体思路详见代码 代码 #include<bits/stdc.h> using namespace std; const int N10; int state[N],path[N];//是否使用过,当前位置 int n; void dfs(int …...
32.代码题
接着上集...... 派对:超时了,总该受到惩罚吧? 洛西:至于吗?就0.1秒! 晴/宇:十分应该。 洛西:我..................... 没办法,洛西只能按照要求去抓R了。 1.P1102 …...
