*当*做C ++异常时会爆炸C代码

时间:2017-09-12 18:13:22

标签: c++ c exception gcc

好的,所以我需要-fexceptions来允许C ++异常通过C代码传播。我试图比较C ++和C的结果,并且从我可以看出,编译的例程在程序集中是相同的,即使没有该选项[1]。这是测试:

#include <unistd.h>

typedef int (*Callback)(void* param,void* buffer,int n);

void write_wrapper(int fd,const void* buffer,int n)
{
    const char* temp=(const char*)buffer;
    while(n!=0)
        {
        int k=write(fd,temp,n);
        n-=k;
        temp+=k;
        }
}

int test(Callback cb,void* cb_param)
{
    char buffer[1024];
    int n=0;
    do
        {
        n=cb(cb_param,buffer,1024);
        write_wrapper(STDOUT_FILENO,buffer,n);
        }
    while(n!=1024);
}

int test2(Callback cb,void* cb_param)
{
char more_stack_space_please[1024]={0};
cb(cb_param,more_stack_space_please,1024);
write_wrapper(STDOUT_FILENO,more_stack_space_please,1024);
test(cb,cb_param);
}

这里,调用者可以释放资源,所以即使回调函数抛出异常也不应该泄漏。

即使此示例似乎有效(使用简单的C ++驱动程序测试)

#include "lib.h"
#include <cstdio>

class Resource
    {
    public:
        Resource()
            {fprintf(stderr,"A Resource\n");}

        ~Resource()
            {fprintf(stderr,"Not a Resource\n");}
    };

int main()
    {
    try
        {
        Resource foo;
        test([](void* cb_param,void* buffer, int n)->int
            {
            Resource bar;
            throw "test";
            },nullptr);
        test2([](void* cb_param,void* buffer, int n)->int
            {
            throw "test2";
            },nullptr);
        }
    catch(const char* err)
        {
        fprintf(stderr,"Error: %s\n",err);
        return -1;
        }
    return 0;
    }

,我从GTK回调中得到了未捕获的异常错误。我的想法:

  • GTK是一个共享库,所以它可能只影响链接器,而不影响代码生成器
  • GTK(和GLib)包含许多奇怪的黑客攻击,可能会搞砸了

[1] https://godbolt.org/g/HfSnjB

1 个答案:

答案 0 :(得分:8)

这个问题的问题在于,它询问未定义结果的定义行为是什么。

C标准没有描述通过C函数抛出异常时的行为,因为它不应该发生。

C ++标准没有解释通过C函数丢弃未捕获异常的机制,因为它不应该发生。

一个实际例子

Windows Visual C ++使用fs:segment寄存器进行线程本地存储,并使用线程本地数据段中的特定插槽来创建catch帧的链接列表。

当抛出C ++异常时,会检查链表以查找堆栈对象的析构函数和合适的catch框架。

C编译器可能不知道这些资源的C ++用法,并且能够为不同目的重新使用这些插槽。如果插槽用于不兼容的功能,则会发生崩溃。

特定的平台和编译器可能会支持这一点,但您可能会在平台上随心所欲。

gcc是一个C编译器,因此不会抛出异常。它可以创建异常感知的代码,因为它支持-fexception

gnu compiler : using exceptions 建议使用-fexceptions编译C代码并说

  

特别是,展开到没有异常处理数据的帧将导致运行时中止。

虽然该语句的上下文没有描述是由于被调试子句,还是由于异常机制识别非法状态并导致中止。

实际的实现是硬件和操作系统特定的,在问题中没有指定,对于特定的答案,应该指定这些。

  • C ++没有兼容性ABI。调用使用不同编译器编译的C ++对象并不能保证工作。
  • 不建议在不同模块之间抛出异常,因为即使编译器上的实现也产生了不符合内部版本的代码。
  • 假设您可以控制C和C ++代码,通过启用异常并可能将C编译为C ++(使用extern "C"包装器来确保C是安全的,确保它是完全C ++编译单元