std::launder、std::bit_cast 与 placement new:合法绕过别名

Linux Swap 子系统:架构、内存管理与虚拟化 Swap Linux Swap机制通过将不活跃内存页移至磁盘或压缩内存,扩展系统有效内存容量,缓解内存压力。传统架构中,逻辑Swap槽与物理存储绑定,导致资源浪费,尤其在使用Zswap等压缩技术时。为此,Vswap与Xswap提出虚拟化方案:前者以XArray实现灵活映射,支持多后端迁移;后者采用稀疏映射,简化管理但容量固定。二者在架构设计、资源记账与动态扩展上各有权衡,反映了内核开发中对性能、可维护性与灵活性的深层考量。 阅读详情

想把一个 std::uint32_t 的位模式按 float 读出来,最顺手的写法是 *reinterpret_cast<float*>(&bits);想在一段内存上先放一个 X、析构后再放一个 Y,最自然的做法是直接复用那个指针。这两个「顺手」的操作,恰恰是 C++ 里最容易踩的**别名(aliasing)**坑:前者违反严格别名规则(strict aliasing),后者让旧指针变成指向「已死对象」的悬垂指针。这篇把三条合法路线 memcpy、std::bit_cast、std::launder 一次讲清,并配真实汇编与 UBSan 报告证明它们的差别。

1. 引子:为什么不能「换个马甲」读同一块内存

先看这段代码。它没有编译错误,-O2 下运行结果通常也是「对的」,但它是未定义行为(undefined behavior):

// 反例,不要这么写:reinterpret_cast 做类型混淆,违反严格别名规则
#include <cstdint>
#include <cstdio>

float read_float(const std::uint32_t bits) {
    return *reinterpret_cast<const float*>(&bits);   // 把 int 的位模式强读成 float
}

int main() {
    std::printf("%f\n", read_float(0x3f800000u));    // 期望 1.0f
}

问题不在「运行结果」,而在编译器对别名的假设。C++ 的严格别名规则规定:通过「不相关类型」的指针去读写同一块内存是 UB。编译器据此大胆假设「float* 和 std::uint32_t* 永远不会指向同一块内存」,于是它可以随意重排、缓存这些访问。一旦你的程序真这么干了,优化器可能把写操作「优化没了」,或读到一个过期的缓存值。这类 bug 极难排查,因为它只在特定优化级别、特定编译版本下才露出马脚。

官方文档:reinterpret_cast · strict aliasing(cppreference 的说明)

2. 严格别名规则:一张表说清能做什么

严格别名的本质是:一块内存只能以它「真实」的类型(加上少数允许的类型)被访问。允许的类型(可以安全地通过指针去读)包括:

内存里实际住着的类型 T可以用哪些类型去访问它
intint、const int、volatile int、unsigned int 对应的「有符号/无符号变体」
floatfloat(不能用 int 去读它的位)
char[] / std::byte[]任意类型(char*/std::byte* 是唯一「万能别名」)
某结构体 SS 本身、S 的第一个成员(标准布局下)
union 的活跃成员只能读活跃的那个成员(type punning 读非活跃成员是 UB)

一句话:char*/std::byte* 可以指向任何东西,其余情况基本只能「同类型或近亲类型」互访。想跨类型读位模式,就得走下面三条合法路线。

3. 路线一:memcpy——最古老、任何标准都能用的合法重解释

std::memcpy 是把「一块内存的字节」复制到「另一块内存」的字节级操作,它天然合法:你从 std::uint32_t 读出 4 个字节、按字节写进一个 float,全程没有「用不相关类型指针直接读写」,编译器不会产生任何错误的别名假设。C++17 就能用:

// memcpy_pun.cpp — 编译: g++ -std=c++17 -Wall -O2 memcpy_pun.cpp -o mp
#include <cstdint>
#include <cstdio>
#include <cstring>

int main() {
    const std::uint32_t bits = 0x3f800000u;   // IEEE-754 里 1.0f 的位模式
    float f{};
    std::memcpy(&f, &bits, sizeof(f));        // 合法:按字节搬运,不违反严格别名

    std::printf("位模式 0x%08x 按 float 读 = %f\n", bits, f);

    // 反方向:float -> uint32_t 也合法
    const float g = -2.5f;
    std::uint32_t g_bits{};
    std::memcpy(&g_bits, &g, sizeof(g_bits));
    std::printf("%f 的位模式 = 0x%08x\n", g, g_bits);
}
位模式 0x3f800000 按 float 读 = 1.000000
-2.500000 的位模式 = 0xc0200000

反向也成立:-2.5f 按位读回整数是 0xc0200000(IEEE-754 的符号位 + 指数 + 尾数拼出来的结果)。memcpy 双向都合法,因为它始终是「字节搬运」,不产生任何类型化的别名访问。

官方文档:std::memcpy

4. 路线二:std::bit_cast——C++20 把 memcpy 规范化成一行

std::memcpy 合法但啰嗦,而且容易写错 sizeof。C++20 引入 std::bit_cast<To>(from),语义等价于一次 memcpy,但:

  • 一行搞定,To 和 From 的字节数必须相等(否则编译期就报错);
  • 是 constexpr,能在编译期做按位重解释(memcpy 不行);
  • 编译器把它优化成和 memcpy 完全一样的机器码(见下文汇编)。
// verify: std=c++20
// bitcast.cpp — 编译: g++ -std=c++20 -Wall -O2 bitcast.cpp -o bc
#include <bit>
#include <cstdint>
#include <cstdio>

int main() {
    const std::uint32_t bits = 0x3f800000u;   // 1.0f
    const float f = std::bit_cast<float>(bits);
    std::printf("bit_cast<float>(0x3f800000) = %f\n", f);

    // 编译期也能用:constexpr 按位重解释
    constexpr std::uint32_t back = std::bit_cast<std::uint32_t>(1.0f);
    static_assert(back == 0x3f800000u, "1.0f 的位模式必须是 0x3f800000");
    std::printf("bit_cast<std::uint32_t>(1.0f) = 0x%08x\n", back);
}
bit_cast<float>(0x3f800000) = 1.000000
bit_cast<std::uint32_t>(1.0f) = 0x3f800000

官方文档:std::bit_cast(需要 C++20)

真实汇编:bit_cast 与 memcpy 零开销

godbolt 上(gcc 13.2,-O2 -std=c++20)把 reinterpret_cast、bit_cast、memcpy 三种写法编译出来对比:

via_reinterpret(unsigned int):
        movd    xmm0, edi
        ret
via_bitcast(unsigned int):
        movd    xmm0, edi
        ret
via_memcpy(unsigned int):
        movd    xmm0, edi
        ret

三个函数一模一样,都是 movd xmm0, edi(把整数寄存器里的位直接搬进浮点寄存器)+ ret。这说明两件事:第一,bit_cast 和 memcpy 没有任何「拷贝」开销,编译器直接把它们优化成一条寄存器搬运;第二,reinterpret_cast 在这里也生成同样的码,但它依然是 UB。差异不在「现在跑出什么」,而在「编译器有没有权利在别处做你意料之外的优化」。所以别用 reinterpret_cast 做类型混淆,不是因为它慢,是因为它给了优化器一把「误解你的代码」的刀。

5. reinterpret_cast 的 UB 会被 UBSan 抓到吗

严格别名违规本身,UBSan(-fsanitize=undefined)不一定能报,它是一种「编译器假设」,不是运行时的非法操作。但 reinterpret_cast 有一类立刻就能被 UBSan 抓到的 UB:未对齐访问(misaligned access)。把 char 缓冲区错开 1 字节,强行 reinterpret_cast 成 int* 再解引用:

// 片段:reinterpret_cast 到未对齐地址 —— 反例,不要这么写
#include <cstdio>

int main() {
    alignas(int) char buf[2 * sizeof(int)];
    char* p = buf + 1;                       // 故意错开 1 字节,未对齐
    int* ip = reinterpret_cast<int*>(p);     // 反例:未对齐的 int 指针
    *ip = 42;                                 // 未对齐写
    std::printf("%d\n", *ip);                 // 未对齐读
}

clang 19 配 -fsanitize=undefined 真跑,stderr 里是:

/app/example.cpp:6:5: runtime error: store to misaligned address 0x7ffc~~ for type 'int', which requires 4 byte alignment
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /app/example.cpp:6:5
/app/example.cpp:7:25: runtime error: load of misaligned address 0x7ffc~~ for type 'int', which requires 4 byte alignment
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior /app/example.cpp:7:25

(地址每次运行都不同,用 ~~ 占位。)这就是 reinterpret_cast 的第二类坑:它只做「位级强制转换」,不检查对齐、不检查类型安全。而 memcpy/bit_cast 因为走的是字节搬运,天然不会产生未对齐的「类型化」访问,也就不会触发这类 UB。

6. 路线三:placement new 重用内存 + std::launder

另一个别名场景和「按位重解释」无关,而是生命周期:你想在同一块存储上先放一个对象,析构后再放一个新的。placement new 让你在不分配新内存的情况下原地构造对象,但它会带来一个悬垂指针问题:指向旧对象的指针不会自动「知道」新对象的到来。

  同一块存储(storage)上,对象的生命周期更替

  时间 ──────────────────────────────────────────────────────────►

  [1] new(storage) X{10}        存储上住着一个 X,p 指向它(有效)
        │
  [2] p->~X()                   显式析构,X 的生命周期结束,p 变悬垂
        │
  [3] new(storage) X{20}        原地重新构造一个新的 X(同类型、同地址)
        │                        ┌─ q 是新构造的,直接用没问题
        │                        └─ p 仍「指向」这块存储,但它指向的旧 X 已死:
        │                           直接 p->n 是 UB(const 成员让编译器可能缓存旧值 10)
        │                           必须 std::launder(p) 才能拿到「新 X」的合法指针
        ▼
   std::launder(p)->n == 20

std::launder(C++17)的职责就是**「洗」掉编译器的过时假设**:它接收一个指向某块存储的指针,返回一个指向「这块存储上当前活着的对象」的指针,明确告诉编译器「别用之前缓存的旧值,重新读」。它在 const 成员、const 引用成员、vptr 这类「编译器会大胆缓存」的场景里不可省略。真跑:

// launder.cpp — 编译: g++ -std=c++17 -Wall -O2 launder.cpp -o launder
#include <cstdio>
#include <new>

struct X {
    const int n;                       // const 成员:编译器有理由缓存 n 的值
    explicit X(int v) : n(v) {}
};

int main() {
    alignas(X) unsigned char storage[sizeof(X)];

    X* p = new (storage) X{10};        // placement new:原地构造,不分配新内存
    std::printf("构造 X,n = %d\n", p->n);

    p->~X();                           // 显式结束 X 的生命周期

    X* q = new (storage) X{20};        // 在同一块存储上重新构造

    std::printf("重新构造 X,q->n = %d\n", q->n);          // q 是新对象,直接读没问题
    std::printf("launder(p)->n = %d\n", std::launder(p)->n); // 旧指针 p 必须洗一下才能用
}
构造 X,n = 10
重新构造 X,q->n = 20
launder(p)->n = 20

注意 q 和 std::launder(p) 返回的其实是「指向同一个新 X」的指针,但含义不同:q 是 placement new 直接给你的新鲜指针,p 是你手里残留的、指向已死对象的旧指针。在有 const 成员的场景,p->n 不带 launder 直接读,编译器完全可能返回缓存的旧值 10(它以为 const 不会变);std::launder(p)->n 才保证读到 20。

官方文档:std::launder(C++17) · placement new

7. 完整示例:一个把三条路线串起来的工具集

把 memcpy、bit_cast、launder 放进一个能整体编译运行的程序,分别演示「按位重解释」和「原地重建对象」两类需求:

// alias_final.cpp — 编译: g++ -std=c++17 -Wall -O2 alias_final.cpp -o afinal
#include <cstdint>
#include <cstdio>
#include <cstring>
#include <new>

// 路线一:memcpy 按位重解释(C++17 通用)
float bits_to_float(std::uint32_t bits) {
    float f{};
    std::memcpy(&f, &bits, sizeof(f));
    return f;
}

// 演示 const 成员的原地重建
struct Cell {
    const int id;
    explicit Cell(int v) : id(v) {}
};

int main() {
    // 1) memcpy:合法按位重解释
    const std::uint32_t one_bits = 0x3f800000u;
    std::printf("memcpy: 0x%08x -> %f\n", one_bits, bits_to_float(one_bits));

    // 2) placement new + launder:同一块存储重建 const 成员对象
    alignas(Cell) unsigned char storage[sizeof(Cell)];
    Cell* c = new (storage) Cell{1};
    c->~Cell();
    new (storage) Cell{2};                       // 重建,新 id = 2
    std::printf("launder 后读到新对象的 id = %d\n", std::launder(c)->id);

    // 3) 反向:把 float 的位模式读回整数(同样用 memcpy)
    const float g = -2.5f;
    std::uint32_t g_bits{};
    std::memcpy(&g_bits, &g, sizeof(g_bits));
    std::printf("%f 的位模式 = 0x%08x\n", g, g_bits);
}
memcpy: 0x3f800000 -> 1.000000
launder 后读到新对象的 id = 2
-2.500000 的位模式 = 0xc0200000

8. 延伸阅读

本知识库内的相关篇目:

9. 一句话总结

跨类型读位模式,reinterpret_cast 是严格别名违规的 UB(哪怕跑起来「看着对」),合法手段是 memcpy(C++17 通用)或 std::bit_cast(C++20,等价且 constexpr),两者都会被优化成零开销的寄存器搬运;原地重建对象后,旧指针指向的是「已死对象」,必须用 std::launder 洗掉编译器的过时缓存假设——这三条路线合起来,就是「合法绕过别名」的完整答案。

第八章 GPU 调度器:8.1 命令提交的演进 本文系统阐述了GPU命令提交的四大演进方向:地址确定、BO就位保障、作业同步机制与队列仲裁权转移。从早期内核主导的ioctl提交,逐步发展为用户态直接写入硬件队列,结合虚拟内存、缺页处理与固件调度,实现提交路径简化与性能提升。各方向相互关联,共同推动内核职责向绑定、驱逐、缺页及固件阶段转移,核心约束——内核对内存访问的最终控制力——始终不变。 阅读详情

相关推荐

利用MATLAB 4f光学系统,在Python中进行弗劳恩霍夫衍射和傅里叶面滤波——用镜头模拟光学图像处理。.zip

1.版本:matlab2014a/2019b/2024b 2.附赠案例数据可直接运行。 3.代码特点:参数化编程、参数可方便更改、代码编程思路清晰、注释明细。 4.适用对象:计算机,电子信息工程、数学等专业的大学生课程设计、期末大作业和毕业设计。

C++实例讲解四种类型转换的使用

C++ 保留了 C 语言的强制类型转换语法(T)exprconst_cast。很多教材只说"新式转换更安全",却没讲清楚安全在哪里、代价是什么,结果读者把所有场合都写成,或者在需要运行时检查的地方继续用 C 风格转换。处理编译期可确定的、有意义的类型关系;处理多态类型的运行时安全向下转换;const_cast只增删constvolatile限定;做位模式层面的重新解释,几乎是"我知道我在干什么"的最后手段。C 风格转换之所以危险,是因为它会按顺序尝试上述几种(还包括const_cast与。

InvictusCoder的博客 6

工业互联网工控安全与工业质检AI大模型建设方案.pptx

工业互联网工控安全与工业质检AI大模型建设方案.pptx

内存管理-Autoreleasepool

Autoreleasepool目前通过@autoreleasepool{}来创建,可以再适当的时机对对象调用release,保证了对象的延迟释放AutoreleasePoolPage底层结构AutoreleasePoolPage是以栈为结点通过双向链表的形式组合而成;遵循先进后出规则,整个自动释放池由一系列的组成的,而是以双向链表的形式连接起来。自动释放池与线程一一对应;调用来入栈,调用来出栈使用哨兵对象来作为出入栈的标志位只是nil的别名。前世叫做POOL_SENTINEL,称为。

ymsjk的博客 343

通信行业CentOS 7内核网络栈优化:高并发连接下UDP/TCP性能调优与系统资源管理

内容概要:本文聚焦于CentOS 7系统在通信行业高并发场景下的性能调优实践,重点围绕内核网络栈优化展开,涵盖UDP/TCP缓冲区调整、文件句柄与连接跟踪表扩容、TIME_WAIT状态复用、软中断分布优化及tuned工具的自动化调优。通过具体配置参数和实际案例(如VoIP软交换系统)深入解析各项调优措施的设计原理与实施效果,有效解决高CPS下丢包、连接失败等问题,并展望了向云化、eBPF等动态智能调优方向的发展趋势。; 适合人群:从事通信系统运维、网络开发或Linux系统性能优化的技术人员,具备一定操作系统和网络基础知识,工作经验1年以上的中初级工程师。; 使用场景及目标:①应用于软交换、媒体网关、信令网关等高并发通信设备的系统调优;②解决UDP丢包、conntrack表满、TCP连接队列溢出等典型性能瓶颈;③提升系统在小包高频、低时延要求场景下的稳定性和吞吐能力。; 阅读建议:建议结合实际生产环境或测试平台同步验证文中配置,重点关注参数背后的物理意义(如BDP、softirq、conntrack机制),并配合ss、conntrack、dmesg等命令进行调优前后对比分析,确保安全可控地实施变更。

2024 《计算机网络》实验教学指导书.doc

2024 《计算机网络》实验教学指导书.doc

【C++】内存管理

本文系统梳理程序内存布局:局部变量在栈,动态分配在堆,全局与静态变量在数据段,字符串字面量在代码段。对比malloc/free与new/delete,强调后者自动调用构造/析构函数,实现内存管理与对象生命周期的统一。详解new[]/delete[]如何记录数组大小并逆序析构,揭示内存泄漏成因——如指针混用、重复释放、未正确配对等,并提出预防策略。核心贯穿问题:空间由谁申请、谁释放、何时构造/析构。

记录在学习过程中学到的知识 328

不平衡电网下自抗扰顺序模型预测PWM整流器控制仿真模型(Simulink仿真实现)

内容概要:本文围绕面向城市空中交通的多无人机路径规划问题展开研究,提出了一套融合全局规划、动态避障与局部协同调整的综合路径规划方法。研究内容涵盖基于改进RRT算法与图论的全局路径规划、基于动态风险场和动态窗口法(DWA)的模型预测控制实现动态避障,以及基于分布式协同控制的多无人机局部路径协同策略。通过Matlab平台构建仿真环境,在多种典型城市交通场景下对所提方法进行了系统性验证,结果表明该方法在路径安全性、编队稳定性与任务效率方面表现优异。文章还深入探讨了算法的创新点与未来研究方向,为复杂城市环境下多无人机系统的自主协同提供了理论支撑与技术路径。; 适合人群:具备自动化、控制理论、计算机科学或相关领域基础知识,从事无人机系统、智能交通、路径规划、多智能体协同控制等方向研究的研究生、科研人员及工程技术人员。; 使用场景及目标:①为城市空中交通系统中的多无人机安全、高效飞行提供可靠的路径规划解决方案;②支持在复杂、动态的城市场景中开展无人机自主导航、编队飞行与协同控制的技术研究与仿真验证。; 阅读建议:建议结合提供的Matlab代码进行实践操作,重点关注算法的具体实现细节、参数配置对性能的影响以及不同仿真场景下的实验设计,以深入理解各模块的集成逻辑与实际应用效果。

C语言与内存管理:枚举

因此,可以用 days_of_week_t today;来声明一个名为 today 的新变量,其类型为 days_of_week_t。enum 本质是"有名字的整数常量集合",所以用法和 int 几乎一样,只是多了可读性和类型名。与结构体一样,enum 定义的是一个类型。

将登太行雪满山 26

【选址和定容】模拟退火改进多目标粒子群算法在分布式电源选址和定容中的应用【IEEE69节点】(Matlab代码实现)

内容概要:本文研究了模拟退火改进多目标粒子群算法在分布式电源选址和定容中的应用,以IEEE69节点配电系统为算例进行仿真分析。通过融合模拟退火算法的强全局搜索能力与粒子群算法的高效局部收敛特性,构建了一种改进的多目标优化算法,用于解决分布式电源在配电网中的最优位置选择与容量配置问题。研究旨在实现多重优化目标,包括最小化网络有功损耗、提升节点电压质量、增强系统运行的经济性与稳定性。文中详细阐述了多目标优化模型的数学构建、算法的改进机制与实现流程,并基于Matlab平台完成了算法编程与仿真验证,结果表明所提方法在求解质量和收敛性能上均优于传统算法。; 适合人群:具备电力系统分析、优化理论基础及Matlab编程能力,从事配电网规划、分布式能源系统优化、智能优化算法应用等相关领域的科研人员、工程师以及高校研究生。; 使用场景及目标:①解决高比例分布式电源接入背景下配电网的科学规划问题;②为新型电力系统中源网协调设计提供有效的优化工具;③为相关领域的科研工作提供可复现、可拓展的算法模型与仿真代码实例,支持进一步的算法对比与功能升级。; 阅读建议:建议读者结合提供的Matlab代码与IEEE69节点标准数据,动手复现实验结果,深入理解算法的参数设置、迭代过程与多目标折衷分析方法,并可根据实际研究需求修改目标函数或引入新的约束条件以适应不同的应用场景。

Anaconda/Miniconda的删除和安装

要在 MacBook 上删除 Anaconda 或 Miniconda,并重新安装它,您可以按照以下步骤进行操作。

qq_27390023的博客 3026

用户态申请内存 vs 内核态申请内存:对系统内存管理的影响

用户态分配的内存与内核态分配的内存,在等方面有本质区别。厘清这一区别,是理解memfdudmabufuserptr、SVM/HMM、dma-buf、CMA、大页等一系列机制的。本文集中论述二者对系统内存管理的影响,并给出典型用例。

图形/计算GPU的驱动与虚拟化技术分享园地 170

COMSOL模拟CO2驱替CH4:多物理场耦合建模与工程应用详解

在非常规天然气开发与碳封存工程中,多孔介质内的渗流、吸附与岩石变形相互耦合,使得CO2驱替CH4的产能预测和CO2封存评估无法仅靠经验公式完成。以达西定律描述裂隙流动、以扩展Langmuir方程刻画竞争吸附、用应力-吸附-渗透率关系关联煤岩变形,是多物理场耦合数值模拟的核心路径。COMSOL Multiphysics凭借内置渗流、固体力学与组分传递接口,能够将这些机制整合为可参数化扫描的仿真模型,为注气压力、井距、渗透率演化等方案设计提供量化依据。围绕CO2驱替CH4模拟的建模流程、参数取法与结果判读,可形

weixin_29574585的博客 158

Java方法学习总结:方法区内存结构总结

本文总结了方法区是JVM规范定义的逻辑区域,用于存储类元数据、常量池等信息,而非固定物理分区。不同JVM实现可采用不同方式:HotSpot JDK 7及以前用永久代,JDK 8后改用元空间,但均满足规范要求。方法区逻辑上属于堆,但物理上可独立于堆内存,其具体实现由JVM自行决定,以适应不同的内存管理策略与操作系统特性。理解方法区的关键在于区分逻辑概念与物理实现。

wuminyu的博客 589

Ubuntu22.04下ROS2与PX4仿真环境搭建全流程解析

本文详细解析了在Ubuntu22.04系统下搭建ROS2与PX4仿真环境的全流程,包括系统准备、ROS2 Humble安装、PX4飞控环境部署、ROS2与PX4通信桥梁搭建等关键步骤。针对常见问题提供了解决方案,并分享了性能优化和开发调试技巧,帮助开发者快速构建无人机仿真开发环境。

weixin_29248653的博客 607

Python爬虫之爬取并下载哔哩哔哩视频

它可以爬取并下载视频文件和音频文件!亲自使用过,太好用了。

2301_80124151的博客 3万+

C 语言基础与内存管理

这篇文章把 C 语言最底层的几块拼图讲清楚:六种基本类型到底占多少内存、一个字符为什么本质是数字、结构体怎么在内存里连续排布、数组为什么省空间,以及操作系统到底怎么管理内存。所有结论都附了本机实测代码和真实输出,不是纸上谈兵。

2401_89953644的博客 322

CentOS 7 常用命令大全

CentOS中Linux命令

m0_63075763的博客 2017

第20讲:处理器下内存管理的发展历程

处理器内存管理历经三阶段演进:实模式下,8086引入分段机制,通过“段基址+偏移量”实现20位寻址,支持程序动态重定位;保护模式中,80386启用GDT与段描述符,实现权限控制(DPL)、地址隔离与虚拟内存调度;最终因段机制导致碎片大、不适应4GB寻址需求,系统转向平坦模式,以页式管理为主。分页机制成为现代内存管理核心,实现高效、安全的虚拟内存管理。

2301_80791727的博客 279

SWAT模型非点源模拟原理

目录引言模型概述非点源污染模拟原理物质迁移内容过程营养物农药病原菌运移河道水质模型营养元素的地表水体循环河道水质模拟藻类模拟氮元素磷元素生化需氧量及溶解氧重金属的运移蓄水体水质模拟 引言 今天无意接触到这一块,就记录下来了。 模型概述 1、流域中非点源污染的来源主要是农业化肥和农药的施用。 2、氮、磷等营养物和农药的迁移和转化取决于化合物在土壤环境中经历的转化过程。 3、SWAT模型可以模拟水文相应单元内氮、磷完整的营养物循环过程和任何一种农药的降解过程。 4、SWAT模拟流域内泥沙、藻类、溶解氧、有机污染

DoWayChan的博客 4877

1006. 换个格式输出整数 (15)-PAT乙级真题

1006. 换个格式输出整数 (15)让我们用字母B来表示“百”、字母S表示“十”,用“12…n”来表示个位数字n(&lt;10),换个格式来输出任一个不超过3位的正整数。例如234应该被输出为BBSSS1234,因为它有2个“百”、3个“十”、以及个位的4。输入格式:每个测试输入包含1个测试用例,给出正整数n(&lt;1000)。输出格式:每个测试用例的输出占一行,用规定的格式输出n。输入样例1...

柳婼 の blog 2242

C++ 内存管理:new 与 delete 详解——从语法到底层 operator new 与 malloc

本文系统解析C++中new与delete的底层机制,从内存分区(栈、堆、静态区等)出发,揭示new=分配内存(operator new→malloc)+构造对象,delete=析构+释放内存(operator delete→free)。对比malloc/free仅管理原始字节、不调构造/析构,阐明new/delete在自定义类型上的关键优势。深入剖析operatornew与malloc关系、定位new原理、异常安全机制及new[]/delete[]配对必要性

octopus_c的博客 539

使用 MATLAB/Simulink + Simscape Electrical,构建一个含光伏并网系统与SVG的联合仿真模型,实现动态无功补偿与电压支撑

摘要:本文详细介绍了基于Simulink的SVG与光伏并网逆变器联合系统建模方法。通过构建包含光伏单元、SVG主电路和控制模块的仿真模型,实现动态无功补偿与电压稳定。重点阐述了ip-iq无功检测算法、双闭环控制和SVPWM调制等关键技术。仿真结果表明,该系统能快速响应无功需求,在10ms内将功率因数提升至0.98以上,有效抑制电压波动。文章为新能源并网系统中的电能质量治理提供了实用解决方案,并提出了进一步优化方向。

xiaoheshang_123的博客 322

《C++深度解构06》手撕 string —— 从底层内存管理到深浅拷贝彻底吃透

本文通过手写C++ string类,深入剖析动态内存管理、深浅拷贝、异常安全及容量控制等核心机制。基于命名空间封装,实现全缺省构造、析构与只读接口,结合reserve动态扩容策略(初始4字节,后续翻倍),确保内存安全与性能优化。重点强调nullptr防护、资源置换安全及静态成员初始化规范,揭示标准库底层逻辑,助力开发者掌握数据结构本质。

huhuiyuan_的博客 387

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

ARM 内存管理

最早期的计算机,程序是打在卡片上的,CPU 读一张卡片执行一条指令,效率极低。后来有了内存,程序要先加载到内存再执行,也就是 “存储程序” 概念,这才开启了操作系统的发展。1. 单道编程的内存管理2. 多道编程的内存管理:解决 “同时跑多个程序” 的问题为了提升资源利用率,出现了。

qq_52240295的博客 321

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

上一篇: perf 与火焰图:从热点到定位(接 Perfetto)
下一篇: C++20 升 C++23 迁移清单:开关、破坏性改动与收益
无名猿
博客等级 码龄10年 61粉丝 184原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值