Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C:如何模拟';例外情况';?_C_Exception Handling_Stack - Fatal编程技术网

C:如何模拟';例外情况';?

C:如何模拟';例外情况';?,c,exception-handling,stack,C,Exception Handling,Stack,我来自C#背景,但我现在正在学习C。在C#中,当一个人想要发出错误发生的信号时,你抛出一个异常。但是你在C语言中做什么 例如,假设您有一个具有push和pop功能的堆栈。在pop期间,发出堆栈为空的信号的最佳方式是什么?从该函数返回什么 double pop(void) { if(sp > 0) return val[--sp]; else { printf("error: stack empty\n"); return 0.

我来自C#背景,但我现在正在学习C。在C#中,当一个人想要发出错误发生的信号时,你抛出一个异常。但是你在C语言中做什么

例如,假设您有一个具有
push
pop
功能的堆栈。在
pop
期间,发出堆栈为空的信号的最佳方式是什么?从该函数返回什么

double pop(void)
{
    if(sp > 0)
        return val[--sp];
    else {
        printf("error: stack empty\n");
        return 0.0;
    }
}

第77页的示例(上面的代码)返回一个
0.0
。但是,如果用户在堆栈的前面按下了
0.0
,您如何知道堆栈是否为空或是否返回了正确的值呢?

一种方法是指定如果堆栈为空,则pop()具有未定义的行为。然后必须提供一个is_empty()函数,可以调用该函数来检查堆栈


另一种方法是使用C++,它有例外:-< /p>< P>异常,类似于C中的行为是通过但是,这里真正需要的是一个错误代码。如果所有值都可能可返回,那么您可能希望输入一个out参数作为指针,并使用它返回值,如下所示:

int pop(double* outval)
{
        if(outval == 0) return -1;
        if(sp > 0)
                *outval = val[--sp];
        else {
                printf("error: stack empty\n");
                return -1;
        }
        return 0;
}
显然,这并不理想,但这就是C语言的局限性


此外,如果你走这条路,你可能想为你的错误代码定义符号常量(或使用其中的一些),这样用户就可以区分“堆栈为空”和“你给了我一个空指针,笨蛋”。

你可以将指针返回到double:

  • 非空->有效
  • 空->无效

您有几个选择:

1) 神奇的错误值。由于你所描述的原因,并不总是足够好。我想在理论上,对于这种情况,您可以返回一个NaN,但我不建议这样做

2) 定义堆栈为空时弹出无效。然后,您的代码要么只是假定它是非空的(如果是,则会变成未定义的),要么断言

3) 更改函数的签名,以便指示成功或失败:

int pop(double *dptr)
{
    if(sp > 0) {
            *dptr = val[--sp];
            return 0;
    } else {
            return 1;
    }
}
将其记录为“如果成功,则返回0并将值写入dptr指向的位置。如果失败,则返回非零值。”

或者,您可以使用返回值或
errno
来指示失败的原因,尽管对于这个特定示例只有一个原因

4) 通过指针将“exception”对象传递给每个函数,并在失败时向其写入一个值。然后调用者根据他们如何使用返回值来检查它。这很像使用“errno”,但它不是线程范围的值


5) 正如其他人所说,使用setjmp/longjmp实现异常。这是可行的,但需要在任何地方传递一个额外的参数(失败时要执行的longjmp的目标),或者将其隐藏在全局中。它还使典型的C风格资源处理成为一场噩梦,因为如果您持有一个负责释放的资源,您不能调用任何可能跳出堆栈级别的东西。

您可以在longjmp/setjmp:之上构建一个异常系统。它实际上运行得很好,这篇文章也是一本很好的读物。如果您使用链接文章中的异常系统,代码的外观如下:

  TRY {
    ...
    THROW(MY_EXCEPTION);
    /* Unreachable */
  } CATCH(MY_EXCEPTION) {
    ...
  } CATCH(OTHER_EXCEPTION) {
    ...
  } FINALLY {
    ...
  }
你能用一个小宏做什么真是太神奇了,对吧?同样令人惊讶的是,如果你还不知道宏是做什么的,那么弄清楚到底发生了什么是多么困难

可移植:C89、C99和POSIX.1-2001指定
setjmp()

注意到,与C或C++中的“实际”异常相比,以这种方式实现的异常仍然有一些限制。一个主要问题是,只有您的代码才能与此异常系统兼容。由于C语言中没有针对异常的既定标准,系统库和第三方库无法与您自己开发的异常系统进行最佳互操作。尽管如此,这有时会成为一种有用的黑客手段


我不建议在严肃的代码中使用这个,除了你自己,其他程序员应该与之合作。如果你不知道到底发生了什么事,那么用这个射自己的脚太容易了。线程、资源管理和信号处理是非玩具程序在尝试使用longjmp“异常”时会遇到的问题领域。

在straight C中没有与异常等价的功能。如果您想要返回错误信息,则必须设计函数签名

C中可用的机制有:

  • 具有setjmp/longjmp的非本地gotos
  • 信号

但是,所有这些都没有与C#(或C++)异常极为相似的语义

  • 交给打电话的人吧。e、 g.由调用方决定是否安全地弹出()(例如,在弹出堆栈之前调用一个stack->is_empty()函数),如果调用方搞错了,那是他的错,祝你好运
  • 通过输出参数或返回值发出错误信号
e、 你也可以

double pop(int *error)
{
  if(sp > 0) {
      return val[--sp];
      *error = 0;
  } else {
     *error = 1;
      printf("error: stack empty\n");
      return 0.0;
  }
}


这里已经有了一些很好的答案,只是想提一下,类似于“异常”的事情可以通过使用宏来完成,就像在awesome中所做的那样(这只会将“异常”返回给调用方函数)。

1)您返回一个标志值以显示它失败,或者使用TryGet语法,其中返回值为布尔值,表示成功,而值通过输出参数传递


2) 如果这是在Windows下,则有一个操作系统级别的纯C形式的异常,称为结构化异常处理,使用类似“\u try”的语法。我提到了它,但我不建议在这种情况下使用它。

这实际上是一个完美的例子,说明了试图用神奇的值重载返回类型的弊端,而只是简单的有问题的接口设计

我可以使用一种解决方案来消除示例中的歧义(因此需要“类似异常的行为”),即定义
int pop(double *d)
{
   if(sp > 0) { 
       *d = val[--sp];
       return 0;
   } else {
     return 1;

   }
}
struct stack{
    double* pData;
    uint32  size;
};

struct popRC{
    double value;
    uint32 size_before_pop;
};

popRC pop(struct stack* pS){
    popRC rc;
    rc.size=pS->size;
    if(rc.size){
        --pS->size;
        rc.value=pS->pData[pS->size];
    }
    return rc;
}
popRC rc = pop(&stack);
if(rc.size_before_pop!=0){
    ....use rc.value
std::pair<something,bool>
std::set<...>::insert
std::map<...>::insert
enum{FAIL,SUCCESS};
int empty(struct stack* pS){
    return (pS->size == 0) ? 1 : 0;
}
int ok=0;

do
{
   /* Do stuff here */

   /* If there is an error */
   break;

   /* If we got to the end without an error */
   ok=1;

} while(0);

if (ok == 0)
{
   printf("Fail.\n");
}
else
{
   printf("Ok.\n");
}