在调试崩溃时,我在一些代码中遇到了这个问题:
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的错误?
答案 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并不是一个明智的想法,你刚刚发现了一个原因。您应该为每个错误调用错误处理函数。