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 c
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:
[#3] EXAMPLE 1 It is sometimes convenient to jump into the
middle of a complicated set of statements. The following
outline presents one possible approach to a problem based on
these three assumptions:
1. The general initialization code accesses objects only
visible to the current function.
2. The general initialization code is too large to
warrant duplication.
3. The code to determine the next operation is at the
head of the loop. (To allow it to be reached by
continue statements, for example.)
/* ... */
goto first_time;
for (;;) {
// determine next operation
/* ... */
if (need to reinitialize) {
// reinitialize-only code
/* ... */
first_time:
// general initialization code
/* ... */
continue;
}
// handle other operations
/* ... */
}
Next, we look at the for loop statement:
[#1] Except for the behavior of a continue statement in the |
loop body, the statement
for ( clause-1 ; expr-2 ; expr-3 ) statement
and the sequence of statements
{
clause-1 ;
while ( expr-2 ) {
statement
expr-3 ;
}
}
Putting the two together with your problem tells you that you are jumping past
i=0;
into the middle of a while loop. You will execute
...process data...
and then
i++;
before flow of control jumps to the test in the while/for loop
i<n;
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). Unless i
has been declared static
, 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:
typedef ... FooState;
void foo(FooState *state, ...)
{
if (FirstCall(state))
{
SetRequest(state, 1);
}
else if (!Done(state))
{
// process data;
SetRequest(state, GetRequest(state) + 1);
}
}
The initialisation part of the for loop will not occur, which makes it somewhat redundant. You need to initialise i
before the goto.
int i = 0 ;
if( not_a_first_call )
goto request_handler;
for( ; i<n; i++)
{
*data_to_request = i;
return;
request_handler:
...process data...
}
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:
int i = 0 ;
if( not_a_first_call )
\\...process_data...
i++ ;
if( i < n )
{
*data_to_request = i;
}
In the end, if you think you need to do this then your design is flawed, and from the fragment posted your logic also.
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.
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 includes i
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.
function foo(int not_a_first_call, int *data_to_request, ...other parameters... )
{
int i = 0;
if( not_a_first_call )
{
...process data...
*data_to_request = i;
return;
}
for (i=0; i<n; i++)
{
*data_to_request = i;
return;
}
}
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?