Home > other >  top level `continue` in nested loop C instead of current level loop
top level `continue` in nested loop C instead of current level loop

Time:08-07

How do I make continue goes to the top level of loop? I mean like this:

for(;;){ // top level loop
   statement1;
   for(;;){ // second level loop
      statement2;
      if (condition1){
         continue; // I expect it continue to the start of top level so it will execute statement1.
      }
   }
   statement3;
}

This is my real case why I got problem above, basically my program is sending data with UDP socket which the data should be sent fast. I know UDP is unrealiable but it's okay the data is tolerated with loss too.

 for (;;) {  // Streaming data...
    camera_fb_t* fb = esp_camera_fb_get();
    size_t quotient = fb ->len / UDP_BUF_SIZE;
    size_t remainder = fb ->len % UDP_BUF_SIZE;
    unsigned int i = 0;
    for (; i < quotient; i  ) {  // sending packet by packet.
      if (uwc_udp_send_raw((const void*)(fb ->buf   (i * UDP_BUF_SIZE)),
                           UDP_BUF_SIZE) < 0) {
        ESP_LOGE(uwc_tag_event, "Error in itteration: %i", i);
        uwc_udp_send_raw("ERR",3); // Tell receiver there was corrupt during send data.
        esp_camera_fb_return(fb);
        continue; // I expect it continue to the top level
      }
    }
    if (remainder) {  // last packet to be sent if remainder exist
      uwc_udp_send_raw((const void*)(fb ->buf   (i * UDP_BUF_SIZE)),
                       remainder);
      ESP_LOGE(uwc_tag_event, "Error in last itteration!");
    }
    esp_camera_fb_return(fb);
  }

CodePudding user response:

This is a rare case where you may use a goto. * Ducks for cover expecting flame-war and down votes *

People will get excited and declare your code automatically unreadable by a single use of goto and generally wring their hands. But in these very rare cases the cleanest and most readable code has a goto. There are exceptions to almost every rule!

Remember, there is almost always a better way than using goto but the real issue with it is that liberal use quickly creates 'spaghetti' code. But a single use can be justified.

#include <stdio.h>

int foo(){
    for(int i=0;i<10;  i){
        for(int j=0;j<10;  j){
            if(j==i*i){
                goto OUTER_LOOP;
            }
        }
        printf("%d\n",i);
OUTER_LOOP: continue;
    }
}

int main(void) {
    foo();
    return 0;
}

People will claim you should set a boolean and break and insist that is 'more readable'. It's no more readable at all. There are few dogmatic laws in programming but "goto is always an necessarily bad" is one of them. It's almost true. But not quite entirely true.

Java has a labelled break statement almost specifically to provide a way of avoiding this use-case for goto and doesn't have goto itself because it has a fix.

I think it's arguable that there is a tiny gap in the language. But in practice this situation is actually quite rare.

CodePudding user response:

You don't need a flag or continue or goto:

for (;;) {
    camera_fb_t *uwcCamFb = esp_camera_fb_get();
    unsigned char *from = uwcCamFb->buf;
    size_t toSend = uwcCamFb->len;

    for( int i = 0; toSend > 0; i   ) {
        size_t sendSize = toSend > UDP_BUF_SIZE ? UDP_BUF_SIZE : toSend;
        if( uwc_udp_send_raw( from, sendSize ) < 0) {
            ESP_LOGE( uwc_tag_event, "Error in itteration: %i", i );
            uwc_udp_send_raw( "ERR", 3 );
            break;
        }
        toSend -= sendSize;
        from  = sendSize;
    }
    if( toSend == 0 )
        esp_camera_fb_return( uwcCamFb );
}

If you need/want to distinguish the LAST packet, add if( toSend < UDP_BUF_SIZE ) to log that particular error message instead...

It seems your OP used both fb and uwcCamFb... I can only guess this corrects that apparently mistake...

(Thank you to @Persixty for a bug report that has been fixed.)

CodePudding user response:

I think goto can solve this problem, I would not use it in normal situation but this is acceptable I think, define a label in the top level for and use goto label in the inner one :]

It's up to you if you want a boolean and check it in every inner loop or use a goto though.

Edit: Honestly 2 for loop are not much, maybe a simple if condition is enough

CodePudding user response:

You need flags:

    for(int i = 0; i < 15; i   )
    {
        int execST3 = 1;
        printf("ST1 i = %d\n", i);

        for(int j = 0; j < 15; j   )
        {
            printf("ST2 j = %d\n", j);
            if(j == 3) {execST3 = 0; break;}
        }
        if(execST3)printf("ST3\n");
    }

CodePudding user response:

The code can be made to work cleanly by replacing the inner infinite loop with a goto style infinite loop. From the compiler point of view that eliminates the second for loop, so that the continue, continues at the first infinite loop.

for (;;)
{
   statement1;
   INNER_LOOP:;
   {
      statement2;
      if (condition1)
      {
         continue; // at the top of the first loop.
      }
      goto INNER_LOOP;
   }
   statement3;
}
  • Related