C/C++: goto into the for loop

前端 未结 7 1609
一向
一向 2021-02-13 21:14

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

相关标签:
7条回答
  • 2021-02-13 21:17

    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;
    
    0 讨论(0)
  • 2021-02-13 21:19

    If I understand correctly, you're trying to do something on the order of:

    • The first time foo is called, it needs to request some data from somewhere else, so it sets up that request and immediately returns;
    • On each subsequent call to foo, it processes the data from the previous request and sets up a new request;
    • This continues until 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);
      }
    }
    
    0 讨论(0)
  • 2021-02-13 21:32

    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.

    0 讨论(0)
  • 2021-02-13 21:33

    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.

    0 讨论(0)
  • 2021-02-13 21:34

    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 iwhich 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; 
        }
    }
    
    0 讨论(0)
  • 2021-02-13 21:36

    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?

    0 讨论(0)
提交回复
热议问题