C/C++: 进入 for 循环
我有一个有点不寻常的情况 - 我想使用 goto 语句跳入循环,而不是跳出循环。
这样做有充分的理由 - 这段代码必须是某个函数的一部分,该函数在第一次调用后进行一些计算,返回并请求新数据,并且需要再调用一次才能继续。不能使用函数指针(明显的解决方案),因为我们需要与不支持函数指针的代码进行互操作。
我想知道下面的代码是否安全,即它会被所有符合标准的 C/C++ 编译器正确编译(我们需要 C 和 C++)。
function foo(int not_a_first_call, int *data_to_request, ...other parameters... )
{
if( not_a_first_call )
goto request_handler;
for(i=0; i<n; i++)
{
*data_to_request = i;
return;
request_handler:
...process data...
}
}
我研究过标准,但没有太多关于此类用例的信息。我还想知道从可移植性的角度来看,用等效的 while 替换 for 是否会有好处。
提前致谢。
UPD:感谢所有发表评论的人!
致所有评论者:) 是的,我知道我无法跳过局部变量的初始值设定项,并且我必须在每次调用时保存/恢复
i
。关于强有力的理由:)这段代码必须实现反向通信接口。反向通信是一种试图避免使用函数指针的编码模式。有时必须使用它,因为遗留代码期望您将使用它。
不幸的是,r-comm-interface 无法以良好的方式实现。您不能使用函数指针,也不能轻松地将工作拆分为多个函数。
I have a bit unusual situation - I want to use goto statement to jump into the loop, not to jump out from it.
There are strong reasons to do so - this code must be part of some function which makes some calculations after the first call, returns with request for new data and needs one more call to continue. Function pointers (obvious solution) can't be used because we need interoperability with code which does not support function pointers.
I want to know whether code below is safe, i.e. it will be correctly compiled by all standard-compliant C/C++ compilers (we need both C and C++).
function foo(int not_a_first_call, int *data_to_request, ...other parameters... )
{
if( not_a_first_call )
goto request_handler;
for(i=0; i<n; i++)
{
*data_to_request = i;
return;
request_handler:
...process data...
}
}
I've studied standards, but there isn't much information about such use case. I also wonder whether replacing for by equivalent while will be beneficial from the portability point of view.
Thanks in advance.
UPD: Thanks to all who've commented!
to all commenters :) yes, I understand that I can't jump over initializers of local variables and that I have to save/restore
i
on each call.about strong reasons :) This code must implement reverse communication interface. Reverse communication is a coding pattern which tries to avoid using function pointers. Sometimes it have to be used because of legacy code which expects that you will use it.
Unfortunately, r-comm-interface can't be implemented in a nice way. You can't use function pointers and you can't easily split work into several functions.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
看起来完全合法。
来自 C99 标准草案 http://std.dkuug。 dk/JTC1/SC22/WG14/www/docs/n843.htm中关于goto语句的部分:
接下来我们看for循环语句:
将两者与您的问题放在一起会告诉您,您正在跳入
while 循环的中间。您将执行
then
before 控制流跳转到 while/for 循环中的测试
Seems perfectly legal.
From a draft of the C99 standard http://std.dkuug.dk/JTC1/SC22/WG14/www/docs/n843.htm in the section on the goto statement:
Next, we look at the for loop statement:
Putting the two together with your problem tells you that you are jumping past
into the middle of a while loop. You will execute
and then
before flow of control jumps to the test in the while/for loop
是的,这是合法的。
你所做的远没有达夫的设备那么丑陋,它也符合标准。
正如 @Alexandre 所说,不要使用
goto
来跳过具有重要构造函数的变量声明。我确信您不希望在调用之间保留局部变量,因为自动变量生存期是如此重要。如果您需要保留某些状态,函子(函数对象)将是一个不错的选择(在 C++ 中)。 C++0x lambda 语法使它们更容易构建。在 C 中,您别无选择,只能将状态存储到调用者通过指针传入的某个状态块中。
Yes, that's legal.
What you're doing is nowhere near as ugly as e.g. Duff's Device, which also is standard-compliant.
As @Alexandre says, don't use
goto
to skip over variable declarations with non-trivial constructors.I'm sure you're not expecting local variables to be preserved across calls, since automatic variable lifetime is so fundamental. If you need some state to be preserved, functors (function objects) would be a good choice (in C++). C++0x lambda syntax makes them even easier to build. In C you'll have no choice but to store state into some state block passed in by pointer by the caller.
首先,我需要说的是,你必须重新考虑以其他方式来做这件事。如果不是因为错误管理。
但如果您确实想坚持下去,则需要记住以下几点:
从循环外部跳转到中间不会使您的代码循环。 (查看下面的评论以获取更多信息)
请小心,不要使用在标签之前设置的变量,例如,参考
*data_to_request
。这包括在 for 语句中设置的i
,并且在跳转到标签时不会初始化。就我个人而言,我认为在这种情况下我宁愿复制
...process data...
的代码,然后使用 goto。如果您仔细观察,您会注意到 for 循环中的 return 语句,这意味着标签的代码永远不会被执行,除非在跳转到它的代码。First, I need to say that you must reconsider doing this some other way. I've rarely seen someone using goto this days if not for error management.
But if you really want to stick with it, there are a few things you'll need to keep in mind:
Jumping from outside the loop to the middle won't make your code loop. (check the comments below for more info)
Be careful and don't use variables that are set before the label, for instance, referring to
*data_to_request
. This includesi
which is set on the for statement and is not initialized when you jump to the label.Personally, I think in this case I would rather duplicate the code for
...process data...
then use goto. And if you pay close attention, you'll notice the return statement inside your for loop, meaning that the code of the label will never get executed unless there's a goto in the code to jump to it.不,你不能这样做。我不知道这到底会做什么,但我确实知道,一旦您返回,您的调用堆栈就会展开,并且变量
i
不再存在。我建议重构。看起来您正在尝试构建一个类似于 C# 中的
yield return
的迭代器函数。也许您实际上可以编写一个 C++ 迭代器来执行此操作?No, you can't do this. I don't know what this will do exactly, but I do know that as soon as you return, your call stack is unwound and the variable
i
doesn't exist anymore.I suggest refactoring. It looks like you're pretty much trying to build an iterator function similar to
yield return
in C#. Perhaps you could actually write a C++ iterator to do this?在我看来,你没有声明
i
。从声明的角度来看,完全取决于您正在做的事情是否合法,但请参阅下面的初始化for
之前声明它,则不会执行对它的0
赋值。goto
之前声明它。在这两种语言中,都有一个更重要的问题,即 i 的值是否定义良好,以及该值是否有意义是否已初始化。
真的,如果有任何方法可以避免这种情况,就不要这样做。或者,如果这确实对性能至关重要,请检查汇编器是否确实满足您的要求。
It seems to me that you didn't declare
i
. From the point of declaration completely depends whether or not this is legal what you are doing, but see below for the initializationfor
the assignment of0
to it will not be performed.goto
.In both languages you have a more important problem, this is if the value of
i
is well defined, and if it is initialized if that value makes sense.Really if there is any way to avoid this, don't do it. Or if this is really, really, performance critical check the assembler if it really does what you want.
如果我理解正确,您正在尝试执行以下操作:
foo
处理完所有数据为止。我不明白为什么在这种情况下你根本需要
for
循环;每次调用只需循环一次(如果我理解这里的用例)。除非i
已被声明为static
,否则每次都会失去它的值。为什么不定义一个类型来维护函数调用之间的所有状态(例如
i
的当前值),然后围绕它定义一个接口来设置/查询您需要的任何参数:If I understand correctly, you're trying to do something on the order of:
foo
is called, it needs to request some data from somewhere else, so it sets up that request and immediately returns;foo
, it processes the data from the previous request and sets up a new request;foo
has processed all the data.I don't understand why you need the
for
loop at all in this case; you're only iterating through the loop once per call (if I understand the use case here). Unlessi
has been declaredstatic
, you lose its value each time through.Why not define a type to maintain all the state (such as the current value of
i
) between function calls, and then define an interface around it to set/query whatever parameters you need:for循环的初始化部分不会发生,这使得它有些多余。您需要在 goto 之前初始化
i
。然而,这确实不是一个好主意!
无论如何,代码都是有缺陷的,return 语句绕过了循环。就目前情况而言,它相当于:
最后,如果您认为需要这样做,那么您的设计就有缺陷,并且从片段中发布了您的逻辑。
The initialisation part of the for loop will not occur, which makes it somewhat redundant. You need to initialise
i
before the goto.However, this is really not a good idea!
The code is flawed in any case, the return statment circumvents the loop. As it stands it is equivalent to:
In the end, if you think you need to do this then your design is flawed, and from the fragment posted your logic also.