23

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

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 实现中的错误?

4

7 回答 7

17

当你跳过一个变量定义时,你可以让 gcc 警告你-Wjump-misses-init,然后你可以使用-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 恰好是一个更明显的问题。

于 2010-05-12T18:35:07.873 回答
9

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

在 C99 中对范围跳转的唯一限制是,跳转到可变修改类型的变量的范围是非法的,比如 VLA

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

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

于 2010-05-12T19:11:24.500 回答
4

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

int func()
{
char *p1 = NULL;    /* So we have a defined 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;
}
于 2010-05-13T15:46:07.967 回答
3

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

于 2010-05-12T18:34:33.930 回答
2

如果我用 -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

没有优化就没有警告

于 2010-05-12T19:16:02.053 回答
0

正如AndreyT 所说,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;
}

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

于 2010-05-12T23:40:56.440 回答
-5

使用 gotos 不是一个聪明的主意,而您刚刚找到了一个原因。您应该为每个单独的错误调用错误处理函数。

于 2010-05-12T19:07:15.057 回答