c99转到过去的初始化

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

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

当你使用-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恰好是一个更明显的函数。

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

在范围方面对C99跳转施加的唯一限制是跳入变量修改类型变量的范围是非法的,如VLA

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

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

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

嗯,这不是因为新标准允许任何地方的变量声明,使用它总是一个好主意。 在你的情况下,我会像在经典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; } 

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

没有优化就没有警告

正如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; } 

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

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