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 实现中的错误?
While debugging a crash, I came across this issue in some code:
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;
}
The problem occurs when the first malloc fails. Because we jump across the initialization of p2
, it contains random data and the call to free(p2)
can crash.
I would expect/hope that this would be treated the same way as in C++ where the compiler does not allow a goto to jump across an initialization.
My question: is jumping across an initialization allowed by the standard or is this a bug in gcc's implementation of c99?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
当您使用
-Wjump-misses-init
跳过变量定义时,您可以要求 gcc 发出警告,然后您可以使用-Werror
(或者更准确地说,< code>-Werror=jump-misses-init) 来强制用户处理它。此警告包含在-Wc++-compat
因此 gcc 开发人员知道代码在 C 和 C++ 中的行为不同。您还可以稍微更改代码:
...并保持将标签与初始化变量配对。使用统一变量调用许多其他函数时,您也会遇到同样的问题,free 只是一个更明显的问题。
You can ask gcc to warn you when you jump over a variable definition by using
-Wjump-misses-init
and then you can use-Werror
(or, more precisely,-Werror=jump-misses-init
) to force the users to deal with it. This warning is included in-Wc++-compat
so the gcc developers are aware that the code behaves differently in C versus C++.You could also change the code slightly:
... and just keep pairing labels with initialized variables. You'll have the same problem with calling many other functions with unitialized variables, free just happens to be a more obvious one.
这样的跳转确实是标准允许的,所以这不是 GCC 中的错误。该标准在附录 I 中将这种情况列为建议警告
。C99 中对范围跳转的唯一限制是,跳转到可变修改类型的变量(如 VLA)的范围是非法的。
换句话说,它说“跳转只是 C 中的跳转”是不正确的。跳转在进入变量作用域时受到一定程度的限制,尽管不像 C++ 中那样严格。您所描述的情况不属于受限制的情况之一。
A jump like that is indeed allowed by the standard, so this is not a bug in GCC. The standard lists this situation as a suggested warning in Annex I.
The only restriction imposed on jumps in C99 with regard to scope is that it is illegal to jump into scope of a variable of variably modified type, like a VLA
In other words, it is not correct to say that "a jump is just a jump in C". Jumps are somewhat restricted when it comes to entering variable scope, albeit not as strictly as in C++. The situation you describe is not one of the restricted ones.
嗯,并不是因为新标准允许在任何地方声明变量,所以使用它总是一个好主意。对于你的情况,我会像我们在经典 C 中所做的那样。
Hmm, it's not because the new standard allows for variable declarations anywhere that it's always a good idea to use it. In your case I would do like we did it in classic C.
这不是 gcc 中的错误。跳转只是 C 中的跳转。没有应用特殊的逻辑。问题是您没有首先将指针初始化为
NULL
。如果你这样做,那么你的免费调用将是free(NULL)
,这不会崩溃。使用char *p1 = NULL, *p2 = NULL;
启动该函数,一切都会好起来的。This is not a bug in gcc. A jump is just a jump in C. There is no special logic applied. The issue is that you are not initializing your pointers to
NULL
first. If you were to do that then you free call would befree(NULL)
which would not crash. Start the function withchar *p1 = NULL, *p2 = NULL;
and all will be well.如果我使用 -O2 标志编译此代码,
我会收到警告:
并且在没有优化的情况下没有警告
if i compile this code with -O2 flag
i've got warning:
and no warning without optimization
正如 AndreyT 所说,C99 允许跳过初始化。您可以通过对两个失败使用单独的标签来修复逻辑:
这是标准模式 - “早期错误”导致跳转到错误退出代码的后续部分。
As AndreyT says, jumping over the initialisation is allowed by C99. You can fix your logic by using seperate labels for the two failures:
This is a standard pattern - "early errors" cause a jump to a later part of the error exit code.
使用 goto 并不是一个聪明的主意,而且您刚刚找到了一个原因。您应该为每个单独的错误调用错误处理函数。
Using gotos is not a smart idea, and you've just found one reason why. You should call an error handling function for each individual error.