C++ 异常是如何实现的

Linux/Unix系统编程 Programming Linux/Unix Systems The Linux ABI The System V ABI Unix System V是Unix的第一个版本。Linux采用了了System V ABI规范并沿用至今。注意这里指的是x86平台,在ARM平台上的ABI并不是完全相同的。 System V AB定义了一个应用程序的底层细节,包括: 寄存器格式 这里仅限介绍Intel-64bit(AMD64)平台。 rip是指令指针,定义指令的当前位置。 rsp是栈指针,rbp是栈头指针,共同定义了 阅读详情

本文内容主要来源于 C++ exceptions under the hood,环境为 gcc/x86,原文非常长且专注于实现自己的异常机制,感兴趣可以看原文,本文只针对于原理介绍与术语讲解。

1、太长不看版总结

  1. 编译器会将 throw 语句翻译成一对 libstdc++ 库里的函数,包括为异常处理分配内存、调用 libstdc 来进行栈展开(stack unwinding)。
  2. 对于每个 catch 语句的存在,编译器会在函数末尾加上一些特殊信息,包括当前函数可以捕获的异常表,以及清理表(cleanup table)。
  3. 在进行栈展开时,会调用 libstdc++ 提供的特殊函数(称为 personality routine),会检查栈上的所有函数哪个异常可以被捕获。
  4. 如果异常无法被捕获,那么 std::terminate 就会被调用。
  5. 如果找到了能够匹配的捕获操作,展开处理(unwinder)会再次在栈顶进行操作。
  6. unwinder 第二次遍历栈时,会要求 personality routine 去为当前函数执行清理操作。
  7. personality routine 会检查当前函数的清理表。如果有什么清理操作要执行的话,就会直接跳到当前栈帧(stack frame),执行清理操作(cleanup code)。这会引起每个在当前作用域分配的对象的析构操作。
  8. 一旦 unwinder 到达了可以处理异常的栈帧时,它会跳到对应的 catch 语句当中。
  9. 执行完 catch 语句后,会调用清理函数去释放掉为异常所分配的内存。

2、throw

2.1、案例分析

尝试在 C 里面用 C++ 的异常机制(即采用纯 C 的链接器来链接 C++ 的 throw 程序),看下会有什么事情发生:

struct Exception {};

extern "C" {
    void seppuku() {
        throw Exception();
    }
}

先正常编译 C 和 C++ 的源代码:

> g++ -c -o throw.o -O0 -ggdb throw.cpp
> gcc -c -o main.o -O0 -ggdb main.c

然后在链接期间就会出现以下错误:

> gcc main.o throw.o -o app
throw.o: In function `foo()':
throw.cpp:4: undefined reference to `__cxa_allocate_exception'
throw.cpp:4: undefined reference to `__cxa_throw'
throw.o:(.rodata._ZTI9Exception[typeinfo for Exception]+0x0): undefined reference to `vtable for __cxxabiv1::__class_type_info'
collect2: ld returned 1 exit status

说明编译器暗中插入了对异常机制进行处理的函数。

2.2、__cxa_allocate_exception

该函数接受一个 size_t 类型的参数,然后为抛出的异常分配内存。

这里的内存分配到哪里是有讲究的,比如说:

  • 栈上(stack) —— 异常机制需要进行栈展开,分配到栈上不合理
  • 堆上(heap) —— 有时可能要抛出爆内存 OOM(out of memory) 的异常,分配到堆上也不合理
  • 静态分配(static) —— 线程不安全
  • 线程局部存储(local thread storage) —— 大部分实现采用这种,若 OOM,则采用特殊应急内存(一般为 static)

2.3、__cxa_throw

一旦异常被创建,该函数就会被调用。

该函数负责进行栈展开的操作,它永远不会返回(return),要么就是跳转到对应的 catch 块去处理异常,要么就是默认地调用 std::terminate 终止程序。

该函数会准备好一大堆东西,然后把异常递交给 _Unwind_ ,是一系列的 libstdc 里的函数进行真正的栈展开操作。

2.4、vtable for cxxabiv1::class_type_info

这个明显就是 RTTI(Run-Time Type Identification) 里的一种,它是用来在运行时判断两种类型是否一致。

在这里,是用来判断一个 catch 是否能够处理(handle)一个 throw。

2.5、自定义简单实现

有了以上这些信息,我们就可以写个简单的代码来提供这些接口:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

namespace __cxxabiv1 {
    struct __class_type_info {
        virtual void foo() {}
    } ti;
}

#define EXCEPTION_BUFF_SIZE 255
char exception_buff[EXCEPTION_BUFF_SIZE];

extern "C" {

void* __cxa_allocate_exception(size_t thrown_size)
{
    printf("alloc ex %i\n", thrown_size);
    if (thrown_size > EXCEPTION_BUFF_SIZE) printf("Exception too big");
    return &exception_buff;
}

void __cxa_free_exception(void *thrown_exception);

#include <unwind.h>
void __cxa_throw(
          void* thrown_exception,
          struct type_info *tinfo,
          void (*dest)(void*))
{
    printf("throw\n");
    // __cxa_throw never returns
    exit(0);
}

} // extern "C"

2.6、汇编查看

用汇编看下编译器所进行的暗中操作:

.LFB3:
    [...]
    call    __cxa_allocate_exception
    movl    $0, 8(%esp)
    movl    $_ZTI9Exception, 4(%esp)
    movl    %eax, (%esp)
    call    __cxa_throw
    [...]

我们看到了对那两个函数的调用,但是编译器还不知道应该怎么处理异常,所以需要能够选择到对应的异常处理函数才行。

3、catch

struct Fake_Exception {};

void raise() {
    throw Exception();
}

// We will analyze what happens if a try block doesn't catch an exception
void try_but_dont_catch() {
    try {
        raise();
    } catch(Fake_Exception&) {
        printf("Running try_but_dont_catch::catch(Fake_Exception)\n");
    }

    printf("try_but_dont_catch handled an exception and resumed execution");
}

同样采用纯 C 的链接器去链接 C++ 的 catch 程序:

> gcc main.o throw.o mycppabi.o -O0 -ggdb -o app
throw.o: In function `try_but_dont_catch()':
throw.cpp:12: undefined reference to `__cxa_begin_catch'
throw.cpp:12: undefined reference to `__cxa_end_catch'

throw.o:(.eh_frame+0x47): undefined reference to `__gxx_personality_v0'

collect2: ld returned 1 exit status

在执行 catch 块代码的时候,会先要调用 __cxa_begin_catch 函数对异常对象进行调整(计数器、放置到栈顶),执行完后会调用 __cxa_end_catch 函数进行异常对象的销毁。

4、Unwinder

__cxa_throw 会准备好一大堆东西,然后把异常递交给 _Unwind_ ,是一系列的 libstdc 里的函数进行真正的栈展开操作。

那么它是怎么找到对应的 catch 块的呢?

异常捕获需要有一定程度的反射(reflexion)的支持(即程序有能力分析它自己的代码)。

用汇编探索下实际的调用情况,为了更加直观,只保留重要的汇编代码。

先看下 raise 函数做了什么:

_Z5raisev:
    call    __cxa_allocate_exception
    call    __cxa_throw

正常地对 throw 异常机制的两个函数进行了调用。

再看下 try_but_dont_catch 函数的情况:

_Z18try_but_dont_catchv:
    .cfi_startproc
    .cfi_personality 0,__gxx_personality_v0
    .cfi_lsda 0,.LLSDA1

链接器会根据 CFI(call frame information) 指令来进行函数的使用判断,CFI 指令信息通常用在栈展开中。

LSDA(language specific data area) 的信息会被 personality 函数使用,用来知悉哪个函数(块)可以处理该异常。

4.1、LSDA

LSDA 的内容包含有:

  • 指向相关数据的指针 —— landing pad start pointer(记录偏移量)、types table pointer(type info 索引)
  • 一个保存了调用点的列表 —— 可能会抛出异常的调用点(call sites)
  • 一个操作记录(action table)的列表 —— catch 块信息、异常的规范

每个来自于 C++ 代码的程序片段都会有自己的 LSDA,它会被加到 .gcc_except_table 当中。

4.2、personality

由于在处理异常时,不同编程语言会存在不同的处理行为,所以异常处理 ABI 提供了一个机制来满足不同的 personality(性格)。

一个异常处理的 personality 会被 personality 函数所定义,比如 C++ 是 __gxx_personality_v0,它会接收异常的上下文,一个异常结构体包含有异常对象的类型和值,以及指向当前函数的异常表(exception table)的引用。

对于当前的编译单元,personality 函数会在异常的栈帧中被指明。

4.3、CFI

CFI(call frame information)实际上是汇编辅助指令(非 CPU 真实指令),用来描述栈帧的结构。

我们需要 CFI,因为手写的汇编代码不会有编译器生成的调试信息,而且为了调试器能够遍历核心文件(core file),或者分析 profilers 能够正确地进行栈展开操作,CFI 都是有必要的。

在异常处理当中,CFI 信息可以用来辅助找到对应的 landing pads 和进行栈展开。

CFI 指令以 .cfi_ 开头。为了进行栈展开,还需要定义 CFA(Canonical Frame Address),代表调用函数在 CALL 指令前 sp(stack pointer,栈指针)的值。我们的任务是定义数据,来使对于给定的任何指令,CFA 都能够被计算出来。

其中一种设计就是 CFI 表,会为每一条指令保存 (register, offset) 的数据对,但为了减少其大小,只保存指令当中被改变的数据。

.globl  square
  .type   square,@function
  .hidden square
square:
  .cfi_startproc                  ; 开始 CFI 记录,.eh_frame 的入口
  push    rbp
  .cfi_adjust_cfa_offset 8        ; 前面有入栈操作,所以更新偏移量
  mov     rbp, rsp
  .cfi_def_cfa_register rbp       ; 用寄存器来定义 CFA 的值
  mov     DWORD PTR [rbp-4], edi
  mov     eax, DWORD PTR [rbp-4]
  imul    eax, DWORD PTR [rbp-4]
  pop     rbp
  .cfi_def_cfa rsp, 8             ; 用寄存器加偏移量的方式定义 CFA 的值
  ret
  .cfi_endproc                    ; 结束 CFI 记录,生成到 .eh_frame 中

4.4、CIE & FDE

CFI 表可以用 objdump 导出为两张表:CIE(Common Information Entry) 和 FDE(Frame Description Entry)。

CIE 表包含所有函数的基本信息:

… CIE
  Version:         1
  Augmentation:    "zR"
  Code alignment factor: 1
  Data alignment factor: -8
  Return address column: 16
  Augmentation data:     1b

  DW_CFA_def_cfa: r7 (rsp) ofs 8
  DW_CFA_offset: r16 (rip) at cfa-8

FDE 表包含函数的 CFI 指令信息:

… FDE cie=…
  DW_CFA_advance_loc: 1 to 0000000000000001
  DW_CFA_def_cfa: r7 (rsp) ofs 16
  DW_CFA_advance_loc: 3 to 0000000000000004
  DW_CFA_def_cfa: r6 (rbp) ofs 16
  DW_CFA_advance_loc: 11 to 000000000000000f
  DW_CFA_def_cfa: r7 (rsp) ofs 8

4.5、try-catch 块

用汇编来看下函数 try-catch 块的行为:

[...]
    call    _Z5raisev          ; raise 函数中调用了 __cxa_throw,正常不会返回
    jmp .L8                    ; 如果是正常函数,则会返回并继续执行
    cmpl    $1, %edx           ; catch 语句对应的起始指令
    je  .L5                    ; 检查异常是否能被处理

.LEHB1:
    call    _Unwind_Resume     ; 不能处理则调用 栈恢复 函数,即清理操作
.LEHE1:

.L5:
    call    __cxa_begin_catch  ; 若能处理,则开始 catch 块的执行
    call    __cxa_end_catch    ; 中间会夹杂着 catch 块的逻辑
.L8:                           ; 函数的末尾
    leave
    .cfi_restore 5
    .cfi_def_cfa 4, 4
    ret
    .cfi_endproc

如果 raise 函数不能正常处理异常,那么它的下一条指令 jmp .L8 就不应该执行,而是应该在异常处理(exception handers)当中,又称之为 landing pad。

4.5.1、landing pad

The term used to define the place where an invoke continues after an exception is called a landing pad.

术语 landing pad 代表:在异常处理当中应该去执行(跳转到)的位置。

landing pads 会有三种:

  • cleanup clause —— 调用 destructors of out-of-scope variables 或 __attribute__((cleanup(...))) 注册的 callbacks,然后调用 _Unwind_Resume 跳转到清理操作
  • catch clause —— 调用 destructors of out-of-scope variables,跳转到 __cxa_begin_catch 调用,然后是 catch 块,最后是 __cxa_end_catch 调用
  • rethrow:调用 destructors of out-of-scope variables in the catch clause,然后调用 __cxa_end_catch,接着用 _Unwind_Resume 跳转回 cleanup phase

在 LLVM 当中,landing pads 是概念上的可选的函数入口(entry points),参数为一个对异常结构体的引用,和一个 type info 的索引。

landing pad 会保存异常结构体的引用,并且会用异常对象对应的 type info 去选择正确 catch 块。

在 LLVM’s exception handling system 当中,会有 ‘landingpad’ 指令来指明一个代码块(basic block)是 landing pad。

;; A landing pad which can catch an integer.
%res = landingpad { i8*, i32 }
         catch i8** @_ZTIi
;; A landing pad that is a cleanup.
%res = landingpad { i8*, i32 }
         cleanup
;; A landing pad which can catch an integer and can only throw a double.
%res = landingpad { i8*, i32 }
         catch i8** @_ZTIi
         filter [1 x i8**] [@_ZTId]

那么如何找到对应的 landing pad,这就要求 _Unwind_ 遍历调用栈,看哪个调用具有合适的带 landing pad 的 try 块可以捕获异常。

4.6、__gcc_except_table

那么 _Unwind_ 是怎么找到合适的 landing pad 的?这时候就需要类似反射的信息的辅助了。

为了知晓 landing pads 在哪里,就用到了 __gcc_except_table,在函数的末尾可以找到:

.LFE1:
    .globl  __gxx_personality_v0
    .section    .gcc_except_table,"a",@progbits
    [...]
.LLSDACSE1:
    .long   _ZTI14Fake_Exception

它会帮助我们来定位 landing pad 被保存到什么位置,实际上是找到 LSDA,然后 personality 函数会检查 LSDA 能不能处理异常。

ELF 文件里 LSDA 通常就保存在 .gcc_except_table 段当中,该段会由 personality 函数来进行解析。

如果为函数指定了 nothrow 的标识符,那么就不会生成该信息,可以减少代码大小,但当异常被抛出时,由于没有 LSDA 的信息,personality 函数不知道该怎么办,通常会调用默认的异常处理机制,所以大概率会调用 std::terminate。

5、two-phase handling

personality 函数的参数含有 action 类型,代表 _Unwind_要求执行什么样的操作,因为捕获异常分为两个阶段:lookup 和 cleanup。

Unwind 会尝试定位异常的 landing pad,而 personality 函数的返回值类型是 _Unwind_Reason_Code,如果是 _URC_HANDLER_FOUND 则代表找到了 landing pad,否则会返回 _URC_CONTINUE_UNWIND 让 Unwind 从下一个栈帧进行尝试。

如果都没找到,那么会调用默认的异常处理机制(std::terminate)。

找到了 landing pad 后,Unwind 会再次遍历栈,调用 personality 函数,采用 _UA_CLEANUP_PHASE 的 action 操作,而 personality 函数会再次检查是否能处理当前的异常。

如果无法处理,则会执行 LSDA 所指定的 cleanup 函数:会执行当前栈上所有对象的析构操作。

如果可以处理,则不会执行 cleanup 函数,会告诉 Unwind 在 landing pad 恢复执行。

为什么 lookup 的时候已经找到了可以处理异常的栈帧,但还要再遍历一次栈,因为这样 personality 函数就有机会对作用域内的对象进行析构操作,从而使得 RAII(Resource Acquisition Is Initialization) 是异常机制安全的操作。

参考

C++ exceptions under the hood:https://monkeywritescode.blogspot.com/p/c-exceptions-under-hood.html

C++ exception handling ABI:https://maskray.me/blog/2020-12-12-c++-exception-handling-abi

Itanium C++ ABI: Exception Handling:https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html

C++异常机制的实现方式和开销分析:http://baiy.cn/doc/cpp/inside_exception.htm?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io

Exception Handling in LLVM:https://llvm.org/docs/ExceptionHandling.html#overview

Personality Function:https://llvm.org/docs/LangRef.html#personalityfn

‘landingpad’ Instruction:https://llvm.org/docs/LangRef.html#i-landingpad

CFI directives in assembly files:https://www.imperialviolet.org/2017/01/18/cfi.html

CFI directives:https://sourceware.org/binutils/docs/as/CFI-directives.html

Exception Handling Tables:https://itanium-cxx-abi.github.io

C++ 异常 目录 1、abort()和exit()函数 2、返回错误码 3、goto跳转指令 4、setjmp和longjmp跳转函数 5、try/catch块 6、try/catch实现原理 7、栈解退 8、异常捕获与继承 9、exception类 10、未捕获异常 11、动态异常说明 12、bad_exception类 13、捕获所有的异常 14、finally语句的替代写法... 阅读详情

相关推荐

C++异常处理实现(libstdc++)

为了更好的理解C++中异常处理的实现,本文简单描述了Itanium ABI中异常处理的流程和llvm/libsdc++简要实现。

梦的灰色边沿... 2259

C++异常的幕后5:围绕__cxa_begin_catch与__cxa_end_catch的魔术

原文地址:https://monoinfinito.wordpress.com/2013/03/05/c-exceptions-under-the-hood-5-magic-around-__cxa_begin_catch-and-__cxa_end_catch/ 作者:nicolasbrailo 在学习了异常如何抛出后,现在我们正在学习如何捕捉它们的过程中。上次我们在我们的例子里添加了一组...

wuhui_gdnt的专栏 1238

C语言中的异常处理实现:cpp-CException

本文还有配套的精品资源,点击获取 简介:在C++中,异常处理是核心特性之一,而C语言并不直接支持这一功能。本项目“cpp-CException”提供了一种C语言中的异常处理实现方式,通过CException库模拟了C++中的 try 、 catch 和 throw 关键字。库内含有宏定义、异常类定义以及栈回溯功能,使得开发者可以在C...

weixin_34413326的博客 1416

std的terminate函数

原文: http://en.cppreference.com/w/cpp/error/terminate 本文翻译比较晦涩,可以只是大致了解下,也可以参考原文。

FinixLei的专栏 (https://github.com/FinixLei) 1万+

Verilog单周期CPU设计(超详细)

实验 单周期CPU一、设计目的与目标实验内容实验要求二、课程设计器材硬件平台软件平台三、 CPU逻辑设计总体方案指令模块MIPS指令格式指令处理流程数据通路总体结构图设计流程逻辑图四、模块详细设计PCAdd4PCINSTMEMDATAMEMSHIFTER32_L2SHIFTER_COMBINATIONMUX4X32MUX2X5EXT16T32MUX2X32CONUNITREGFILEALUSing...

Ace Cheney的博客 9万+

Android BluetoothSocket

BluetoothSocket是Android中用于在蓝牙设备之间进行数据传输的一种Socket。它提供了一种类似于TCP/IP协议的数据传输方式,可以在两个设备之间建立一个虚拟的通信管道,使得两个设备之间可以进行数据的传输和接收。BluetoothSocket可以用于客户端和服务端之间的通信,通过BluetoothSocket可以实现数据的双向传输。

liuning1985622的博客 1477

undefined reference to `vtable for __cxxabiv1::__class_type_info'

今天编译一个程序需要使用虚函数的类,想探索一下虚函数表的结构,结果总是在链接的时候报错如下: undefined reference to `vtable for __cxxabiv1::__class_type_info' 编译命令是:gcc virtualtable.cpp -o virtualtable 经过探索,原来是在链接的时候,需要使用C++的库,而默认是不使用的,所以必须添...

jimmyleeee的专栏 4531

反激电源占空比为何必须小于0.5?次谐波振荡与斜坡补偿详解

在开关电源设计中,峰值电流模式控制因其优异的动态响应和逐周期限流保护而被广泛应用。其工作原理是通过采样电感电流的峰值,与误差电压比较来控制开关管的导通与关断。然而,这种控制方式在反激等隔离拓扑中,当占空比超过50%时,会面临一个固有的稳定性挑战——次谐波振荡。这是由于电流扰动会在相邻开关周期内被放大而非衰减,导致系统在开关频率一半处发生振荡,严重影响输出纹波和可靠性。为解决这一问题,工程上引入了斜坡补偿技术,通过向控制信号叠加一个固定斜率的斜坡,改变环路增益条件,从而在宽输入电压或高占空比需求下维持系统稳定

weixin_29631169的博客 625

yaml-cpp异常安全保证:强异常安全与基本异常安全实现终极指南

yaml-cpp作为一个功能强大的C++ YAML解析器和发射器库,提供了完善的异常安全保证机制。本文将深入探讨yaml-cpp如何通过智能指针管理、RAII模式、noexcept修饰符和精心设计的异常层次结构,为开发者提供可靠的异常安全保证。了解yaml-cpp的异常安全实现对于构建稳定可靠的C++应用程序至关重要。 ## 📊 yaml-cpp异常安全架构概览 yaml-cpp采用了分层异

gitblog_01120的博客 461

小米万兆路由器里的Docker安装可道云(Kodexplorer)私有网盘

小米2022年12月份发布了万兆路由器,里面可以使用Docker。 今天尝试在小米的万兆路由器里安装[可道云](http://kodcloud.com/)(Kodexplorer)私有网盘。

engchina的专栏 1万+

用「OpenClaw」打造新媒体运营神器:小红书/公众号全自动选题→创作→排版→发布全流程

对于新媒体运营的从业者来说,AI不是要取代运营人员,而是把他们从繁琐的重复劳动中解放出来,专注于更核心的内容策略、用户运营、品牌建设,这才是AI在传统行业落地的真正价值。这是最终输出可发布内容的核心智能体,我们通过「OpenClaw」的Prompt工程,结合不同平台的排版规范,实现了一键生成符合要求的排版,完全替代人工重复劳动。这套系统的落地,不是技术的炫技,而是真正用AI解决了新媒体运营的核心痛点:用自动化替代重复的人工劳动,用数据决策替代经验主义,用全闭环执行替代纸上谈兵的建议。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 198

yaml-cpp容错解析架构深度解析:从异常处理到部分解析的C++实现

在复杂的软件系统中,配置文件解析的健壮性直接影响应用的稳定性。yaml-cpp作为C++生态中功能最完备的YAML解析库,其错误恢复机制设计巧妙,能够在遇到格式问题时依然保持最大程度的可用性。本文将深入剖析yaml-cpp的容错架构,揭示其如何在异常情况下实现部分解析与智能错误报告。 ## 异常处理体系架构设计 yaml-cpp构建了层次分明的异常处理体系,将错误分为三个主要类别: | 异常

gitblog_01084的博客 477

国产系统(统信 UOS )漏洞修复方式

2.1 服务器版(单漏洞更新):服务器标准版:yum upgrade-minimal --cve “CVE编号”服务器 D 版:sudo apt update&&sudo apt install “受影响的软件包”1 统信 UOS 漏洞修复方式在线升级修复桌面专业版:通过以下步骤打开在线升级:控制中心–>更新–>更新设置–>打开系统更新、安全更新。安装过程中,请勾选“我已阅读并知晓”,然后点击“开始升级”。2 点击“检查更新”–“全部下载”,如果不想进行系统更新,可以选择只下载“安全更新”。

Reyn_观极 5012

Duktape 中的 C++ 异常机制:用 `DUK_USE_CPP_EXCEPTIONS` 实现长控制转移与 RAII 析构保障

Duktape 默认使用 `setjmp()` / `longjmp()` 实现内部的长控制转移(long control transfer),但这类跳转会直接绕过 C++ 的栈展开(stack unwinding),导致 Duktape/C 函数中的自动变量析构函数(即基于作用域的 RAII 资源管理)不会执行。本文围绕 [examples/cpp-exceptions/README.rst](

gitblog_01170的博客 191

【腾讯小龙虾WorkBuddy专栏02】保姆级安装教程!彻底分清WorkBuddy/CodeBuddy,最新积分活动&会员体系全攻略

【腾讯小龙虾WorkBuddy专栏02】保姆级安装教程!彻底分清WorkBuddy/CodeBuddy,最新积分活动&会员体系全攻略

AI人人懂 2587

STM32入门教程:WiFi通信

第四步:初始化串口 在生成的代码中,您可以找到一个名为 "USARTx_Init" 的函数,其中 "x" 是您选择的串口号。然后,我们使用 "AT+CIPSEND" 命令发送UDP数据,接收到服务器的回复,并在终端上显示收到的数据。在配置向导中,您可以选择启用USART串口功能,并根据您的硬件连接,将串口设置为与WiFi模块连接的串口。在上述代码中,我们使用了 "HAL_UART_Receive" 函数来接收串口数据,并使用 printf 来在终端上显示收到的数据。如果有任何问题,请随时向我提问。

2401_85258012的博客 1073

Duktape 2.7.0 C++ 异常模式解析:用 DUK_USE_CPP_EXCEPTIONS 取代 setjmp/longjmp 实现安全的长控制转移

Duktape 默认使用 `setjmp()` / `longjmp()` 实现内部的长控制转移(long control transfers,即从 JS 执行栈深处直接跳转到错误处理点),这会导致 C++ 程序中最关键的保障失效:**Duktape/C 函数栈上的自动对象析构函数不会被调用**,基于 RAII(Resource Acquisition Is Initialization,资源获取

gitblog_01109的博客 299

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

C++编译器怎么实现异常处理1

C++编译器怎么实现异常处理 对于VC++实现异常处理的深入探讨 导论 相比较其他传统的语言,C++的一个变革的特征是支持异常处理。相对于传统语言的不清楚容易错误的错误处理机制,C++的异常处理是一个非常好的替代。在正常的代码和错误处理代码之间清楚的分割使得程序非常整洁和宜于维护。本文讨论编译器怎么实现异常处理。假设读者熟悉异常处理的语法。 本文包含一个异常处理的VC++的

sdssly的专栏 1404

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

C++ 异常处理机制的实现

介绍 相对于传统语言,C++ 的革命性特征之一,就是它对异常处理的支持。传统异常处理技术有缺陷并且易于出错,而 C++ 提供了一个非常优秀的替代方案。它将正常流程代码与错误处理代码清晰的隔离出来,使得程序更加健壮,易于维护。这篇文章将讨论编译器是如何实现异常处理的。假定读者已经对异常处理机制及其语法已经有了大致的了解。我用 VC++ 实现了本文中介绍的异常处理库。要将异常处理器替换成我的 VC+...

826

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

C++异常实现机制

1、C函数的调用和返回   要理解C++异常机制实现之前,首先要了解一个函数的调用和返回机制,这里面就要涉及到ESP和EBP寄存器。我们先看一下函数调用和返回的流程。 下面是按调用约定__stdcall 调用函数test(int p1,int p2)的汇编代码 假设执行函数前堆栈指针ESP为NN push p2 ;参数2入栈, ESP -= 4h , ESP = N...

aifu7243的博客 495

C++异常处理的实现机制解析

C++ 的异常机制是如何实现的?请简要说明异常抛出、传递与处理的过程。C++ 异常机制是一种结构化的错误处理方案,它通过trythrow和catch实现错误的检测、抛出与捕获。异常机制使得,提高了代码的可维护性和健壮性。

陈子青的博客 352

【链接】C++编译器是如何实现异常处理的

在Codeproject看到一篇好文,How a C++ compiler implements exception handling(C++编译器是如何实现异常处理的,但是仅讨论了VC),作者Vishai Kochhar(很奇怪的名字)。http://www.codeproject.com/KB/cpp/exceptionhandler.aspx作者在这篇文章里介绍Windows里的系统级异

lingol的专栏 597

C++异常机制的实现方式和开销分析

<br /><br />在我几年前开始写《C++编码规范与指导》一文时,就已经规划着要加入这样一篇讨论 C++ 异常机制的文章了。没想到时隔几年以后才有机会把这个尾巴补完 :-)。<br />还是那句开场白:“在恰当的场合使用恰当的特性” 对每个称职的 C++ 程序员来说都是一个基本标准。想要做到这点,就必须要了解语言中每个特性的实现方式及其时空开销。异常处理由于涉及大量底层内容,向来是 C++ 各种高级机制中较难理解和透彻掌握的部分。本文将在尽量少引入底层细节的前提下,讨论 C++ 中这一崭新特性,并分析

梦想狂奔的专栏 512

C++ Insights异常处理机制:try-catch块的底层实现

还在为C++异常处理的复杂性而头疼?想知道编译器是如何将你的try-catch代码转换为可执行形式的吗?C++ Insights这个神奇工具能让你一窥编译器背后的魔法! ## 异常处理的编译器视角 C++ Insights是一个基于Clang的源代码转换工具,它能展示编译器如何处理你的C++代码。对于异常处理机制,它揭示了try-catch块在编译器眼中的真实面貌。 ### try-catc

gitblog_00219的博客 672

C++中 异常处理的 实现方式

1、异常是什么? 程序执行期间出现的错误或意外情况。   2、传统的异常处理方式 条件判断、分支处理 if 优点:实现简单直观 缺点:a、种类及情况太多,在大型系统中很难全部考虑到,导致程序崩溃,终止运行 b、某些异常情况的发生具有随机性,无法在程序中做出明确的处理,但是我们有时候又希望程序能够跳过异常部分错误运行。 c、我们希望可以传递异常信息,也就是发生

做一个快乐的人 957
上一篇: SOLID 代码设计原则
下一篇: LLVM 新一代 JIT API:ORC
流左沙
博客等级 码龄11年 46粉丝 55原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值