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函数。