如何在C中引发异常?


100

我在Google中输入了此字词,但只在C ++中找到了howtos,

用C怎么做?


6
C不支持异常处理。要在C语言中引发异常,您需要使用特定于平台的内容,例如Win32的结构化异常处理-但要提供任何帮助,我们需要了解您关注的平台。
杰里·科芬

18
...并且不要使用Win32结构化异常处理。
Brian R. Bondy

4
从理论上讲,使用setjmp()和longjmp()应该可以工作,但我认为这样做不值得。
约瑟夫·昆西

Answers:


77

C中没有异常。在C中,错误是通过函数的返回值,进程的退出值,发给进程的信号(程序错误信号(GNU libc))或CPU硬件中断(或其他通知)来通知的(如果有的话)从CPU产生错误)(处理器如何处理被零除的情况)。

例外是用C ++和其他语言定义的。C ++中的异常处理在C ++标准“ S.15异常处理”中指定,C标准中没有等效部分。


4
因此,用C保证不会有异常,怎么办?
httpinterpret

62
@httpinterpret:在C语言中,“保证”没有异常,就像在“保证”中没有模板,没有反射或没有独角兽一样。语言规范根本没有定义任何此类内容。但是有setjmp / longjmp,可用于退出函数而无需返回。因此,程序可以根据需要构建自己的类似于异常的机制,或者C实现可以定义对标准的扩展。
史蒂夫·杰索普

2
与程序main.c文件可以包含一些C ++,因此异常可能会被抛出,并夹在程序中,但C代码部分仍将无知这一切的要去除了异常抛出的和捕获往往依赖于C写的函数驻留在C ++库中。之所以使用C,是因为您不必冒险调用throw需要自己抛出异常的函数。但是,可能存在编译器/库/目标特定的引发/捕获异常的方式。但是抛出一个类实例将有它自己的问题。
nategoose 2010年

61
@Steve:如果您发现独角兽语言,请让我知道,我已经等了这么多年了。
Brian R. Bondy 2010年

5
@ BrianR.Bondy 是具有独角兽的语言(我保证与C语言相同)
Tofandel 2014年

37

在C中,您可以使用setjmp()和中longjmp()定义的功能的组合setjmp.h维基百科的例子

#include <stdio.h>
#include <setjmp.h>

static jmp_buf buf;

void second(void) {
    printf("second\n");         // prints
    longjmp(buf,1);             // jumps back to where setjmp 
                                //   was called - making setjmp now return 1
}

void first(void) {
    second();
    printf("first\n");          // does not print
}

int main() {   
    if ( ! setjmp(buf) ) {
        first();                // when executed, setjmp returns 0
    } else {                    // when longjmp jumps back, setjmp returns 1
        printf("main");         // prints
    }

    return 0;
}

注意:实际上,我建议您不要使用它们,因为它们无法在C ++上正常工作(不会调用本地对象的析构函数),并且确实很难理解发生了什么。而是返回某种错误。


我已经看到setjump / longjump在C ++程序中无法正常工作,即使在使用异常时没有对象需要销毁的情况下也是如此。我很少在C程序中使用它们。
nategoose 2010年

这是使用setjmp的一种有趣的方法。on-time.com/ddj0011.htm但是,是的,如果要执行带外代码执行而又不放松堆栈,则基本上必须自己发明它们。
Dwayne Robinson

我不确定为什么在有关C和C ++的问题时有关在C ++中使用它们的警告仍然有例外。无论如何,重要的是,OP必须知道为了使setjmp / longjmp实现免于失败,请务必记住,您首先需要访问错误处理程序(进行设置)以及该错误处理程序需要返回两次。

1
顺便说一句,异常处理是我真正希望能够在C11中结束的事情。尽管共同的信念,它并不需要OOP才能正常,和C的每一个函数调用需要的无端遭受if()。我看不到里面有危险。有人可以在这里吗?

@ user4229245我在(轶事)印象中,“为您完成”的任何事情都尽可能地不符合c语言规范,尤其是要使c与裸机和机器编程相关,即使是像8位字节这样的现状基础也是如此。不是普遍的,只是我的想法,我不是c规范作者
ThorSummoner


20

C中没有内置的异常机制。您需要模拟异常及其语义。这通常是依靠setjmp和来实现的longjmp

周围有很多库,而我正在实现另一个。它称为exceptions4c;它是便携式且免费的。您可以查看一下,然后将其与其他替代方案进行比较,以找出最适合您的方案。


我读了代码示例,我从一个结构开始了一个类似的项目,但是使用uuid而不是字符串来标识每个“异常”。您的项目似乎很有希望。
umlcat


您(或其他任何人)是否知道不同异常包之间的比较?
Marcel Waldvogel

11

C能够引发C ++异常,无论如何它们都是机器代码。例如,在bar.c中

// begin bar.c
#include <stdlib.h>
#include <stdint.h>
extern void *__cxa_allocate_exception(size_t thrown_size);
extern void __cxa_throw (void *thrown_exception, void* *tinfo, void (*dest) (void *) );
extern void * _ZTIl; // typeinfo of long
int bar1()
{
   int64_t * p = (int64_t*)__cxa_allocate_exception(8);
   *p = 1976;
   __cxa_throw(p,&_ZTIl,0);
  return 10;
}
// end bar.c

在a.cc中

#include <stdint.h>
#include <cstdio>
extern "C" int bar1();
void foo()
{
  try{
    bar1();
  }catch(int64_t x){
    printf("good %ld",x);
  }
}
int main(int argc, char *argv[])
{
  foo();
  return 0;
}

编译它

gcc -o bar.o -c bar.c && g++ a.cc bar.o && ./a.out

输出

good 1976

http://mentorembedded.github.io/cxx-abi/abi-eh.html提供了有关的更多详细信息__cxa_throw

我不确定它是否可移植,并在Linux上使用“ gcc-4.8.2”对其进行了测试。


2
到底是什么“ _ZTIl” ???我在任何地方都找不到对它的引用,这完全是个谜,如何使C代码能够访问std :: type_info。请帮我!
AreusAstarte '18

1
_ZTII手段typeinfo for long,例如echo '_ZTIl' | c++filt 。这是g ++处理方案。
wcy

并且为了确保一切正常,请务必在catch块中调用ac符号,以尽可能避免使用c ++:P(PS感谢您分享一个出色的真实示例!)
ThorSummoner

8

这个问题太老了,但我偶然发现了这个问题,以为我会分享一种技术:除以零或取消引用空指针。

问题仅是“如何引发”,而不是如何捕捉甚至如何引发特定类型的异常。我很久以前就遇到过这样的情况,我们需要触发C的异常才能被C ++捕获。具体来说,我们偶尔会报告“纯虚函数调用”错误,因此需要说服C运行时的_purecall函数抛出某些错误。因此,我们添加了自己的_purecall函数,该函数除以零,再加上繁荣,我们得到了一个例外,可以捕获C ++,甚至可以利用一些堆栈乐趣来查看问题出在哪里。


@AreusAstarte,以防万一有人真的需要被C除以零:int div_by_nil(int x) { return x/0; }

7

在带有MSVC的Win上,__try ... __except ...它确实很可怕,如果可以避免的话,您就不想使用它。最好说没有例外。


1
实际上,C ++异常也是在SEH的顶部构建的。
Calmarius

@Calmarius什么是SEH
Marcel Waldvogel

1
@MarcelWaldvogel结构化异常处理(Windows处理CPU异常的方式)。
Calmarius


3

如许多线程中所述,执行此操作的“标准”方法是使用setjmp / longjmp。我在https://github.com/psevon/exceptions-and-raii-in-c中发布了另一个这样的解决方案,据 我所知,这是唯一依靠自动清除分配的资源的解决方案。它实现了唯一且共享的智能指针,并允许中间函数让异常通过而不会捕获,并且仍可以正确清理其本地分配的资源。


2

C不支持异常。您可以尝试使用Visual Studio或G ++将C代码编译为C ++,然后查看它是否可以原样编译。大多数C应用程序都可以在不进行重大更改的情况下作为C ++进行编译,然后可以使用try ... catch语法。


0

如果您使用快乐路径设计模式(即,针对嵌入式设备)编写代码,则可以使用运算符“ goto”来模拟异常错误处理(即延迟或最终仿真)。

int process(int port)
{
    int rc;
    int fd1;
    int fd2;

    fd1 = open("/dev/...", ...);
    if (fd1 == -1) {
      rc = -1;
      goto out;
    }

    fd2 = open("/dev/...", ...);
    if (fd2 == -1) {
      rc = -1;
      goto out;
    }

    // Do some with fd1 and fd2 for example write(f2, read(fd1))

    rc = 0;

   out:

    //if (rc != 0) {
        (void)close(fd1);
        (void)close(fd2);
    //}

    return rc;
}

它实际上不是异常处理程序,但是它为您提供了一种在功能退出时处理错误的方法。

PS:应仅在相同或更深的范围内谨慎使用goto,切勿跳转变量声明。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.