c99转到过去的初始化

时间:2010-05-12 18:27:43

标签: c gcc c99 goto

在调试崩溃时,我在一些代码中遇到了这个问题:

int func()
{
    char *p1 = malloc(...);
    if (p1 == NULL)
        goto err_exit;

    char *p2 = malloc(...);
    if (p2 == NULL)
        goto err_exit;

    ...

err_exit:
    free(p2);
    free(p1);

    return -1;
}

第一个malloc失败时会出现问题。因为我们跳过p2的初始化,它包含随机数据,对free(p2)的调用可能会崩溃。

我希望/希望这与C ++中的处理方式相同,其中编译器不允许goto跳过初始化。

我的问题:是跳过标准允许的初始化还是这是gcc实现c99的错误?

7 个答案:

答案 0 :(得分:17)

当您使用-Wjump-misses-init跳过变量定义时,您可以要求gcc发出警告,然后您可以使用-Werror(或更确切地说,-Werror=jump-misses-init)强制用户处理它。此警告包含在-Wc++-compat中,因此gcc开发人员知道代码在C与C ++中的行为不同。

您也可以稍微更改代码:

int func()
{
    char *p1 = malloc(...);
    if (p1 == NULL)
        goto err_exit_1;

    char *p2 = malloc(...);
    if (p2 == NULL)
        goto err_exit_2;

    ...

err_exit_2:
    free(p2);
err_exit_1:
    free(p1);

    return -1;
}

...并保持标签与初始化变量配对。使用单元化变量调用许多其他函数时会遇到同样的问题,free恰好是一个更明显的函数。

答案 1 :(得分:9)

这样的跳跃确实是标准允许的,所以这不是GCC中的错误。该标准将此情况列为附件I中的建议警告。

对于范围而言,C99跳转的唯一限制是跳入变量修改类型的变量范围是非法的,如VLA

int main() {
  int n = 5;
  goto label; // <- ERROR: illegal jump
  int a[n];
label:;
}

换句话说,说“跳跃只是C中的跳跃”是不正确的。在进入变量范围时,跳转受到一些限制,尽管不像C ++那样严格。您描述的情况不是受限制的情况之一。

答案 2 :(得分:3)

这不是gcc中的错误。跳转只是C中的跳跃。没有应用特殊逻辑。问题是您没有首先初始化指向NULL的指针。如果您这样做,那么您的免费通话将是free(NULL),这不会崩溃。使用char *p1 = NULL, *p2 = NULL;启动该功能,一切顺利。

答案 3 :(得分:3)

嗯,这不是因为新标准允许在任何地方使用变量声明,因此使用它总是一个好主意。在你的情况下,我会像在经典C中那样做。

int func()
{
char *p1 = NULL;    /* So we have a definite value */
char *p2 = NULL;

  p1 = malloc(...);
  if(!p1)
    goto err_exit;

  p2 = malloc(...);
  if(!p2)
    goto err_exit;

  ...

  err_exit:
    free(p2);
    free(p1);

  return -1;
}

答案 4 :(得分:1)

如果我使用-O2标志编译此代码

gcc -Wall -std=c99 -O2 jump.c

我收到了警告:

jump.c: In function ‘func’:
jump.c:10: warning: ‘p2’ may be used uninitialised in this function

没有优化就没有警告

答案 5 :(得分:0)

作为AndreyT says,C99允许跳过初始化。您可以通过为两个故障使用单独的标签来修复逻辑:

int func()
{
    char *p1 = malloc(...);
    if (p1 == NULL)
        goto err_exit_p1;

    char *p2 = malloc(...);
    if (p2 == NULL)
        goto err_exit;

    ...

err_exit:
    free(p2);
err_exit_p1:
    free(p1);

    return -1;
}

这是一种标准模式 - “早期错误”导致跳转到错误退出代码的后期部分。

答案 6 :(得分:-5)

使用gotos并不是一个明智的想法,你刚刚发现了一个原因。您应该为每个错误调用错误处理函数。