Finally wrote my own program in C, with loop and goto

Discussion in 'Mac Programming' started by cybrscot, Feb 28, 2011.

  1. cybrscot macrumors 6502

    cybrscot

    Joined:
    Dec 7, 2010
    Location:
    Somewhere in Southeast Asia
    #1
    Aha! I'm only posting because I'm quite happy that I just wrote my own program! Not required by the book. I decided I'd write a program to figure out the remainder. % of two numbers entered by a user (me). I'm pleased because I wrote it in only about 15 minutes! I feel like I'm getting better at understanding this stuff, and I know in some cases how to use the code I've learned so far to get it to do what I want it to do.

    My primary problem is still understanding what the book wants me to do in certain (many, as you know) problems. I think when the problem is mine, I'm already a few steps ahead in the process of how to think about it and break it down (smaller parts) into what I want and in writing pseudo code. I guess I've got some trouble comprehending and understanding what the book wants sometimes.

    Anyway, here it is for all to see! A small victory for me in my quest for C! Hey that rhymed!:)

    Code:
    #include <stdio.h>
    
    main ()
    
    {
    	int remainder, numerator, denominator ;
    	printf ("This program computes the remainder of user entered numbers," ) ;
    	printf ("0 to exit\n") ;
    	
    	do {
    	printf ("Enter a number: ") ;
    	scanf ("%d", &numerator) ;
    	if (numerator == 0) goto done;
    	printf ("Enter a number: ") ;
    	scanf ("%d", &denominator) ;
    	
    	remainder = numerator % denominator ;
    	
    	
    	printf ("%d\n", remainder) ;
    	} while (numerator != 0); 
    	
    	done: printf ("Thank you, have a nice day\n") ;
    	
    	return 0;
    	
    }	
     
  2. mydogisbox macrumors member

    Joined:
    Jan 16, 2011
    #2
    Figuring things out is one of the best parts. :) I would recommend using a break statement or an if statement instead of the less-clear goto.
     
  3. cybrscot thread starter macrumors 6502

    cybrscot

    Joined:
    Dec 7, 2010
    Location:
    Somewhere in Southeast Asia
    #3
    Cool. I've never used a goto before, and all break statements are covered in my chapter. So I figured I'd at least get some experience with each, at least once, before I move on and maybe never use goto again. My book also mentioned that goto is not often used.

    What I want to know from you is why do you consider the goto less clear? In my thinking based on my level of knowledge, it's about the most clear thing I've used because of the label. It says goto (label), and then you find the label, and it went there. Seems very precise and easy to read.

    Tell me more, please.

    Thanks
    Scott
     
  4. mydogisbox macrumors member

    Joined:
    Jan 16, 2011
    #4
    This has to do more with readability and maintainability than codeability. You understand the logic of the code that you just wrote, but it's less obvious to someone else trying to read it, whereas an if statement or a break statement has a certain logic build into it. One thing that isn't immediately obvious when you start programming with a higher-level programming language like C is that all statements are compiled down to gotos and such eventually. A loop is simply a goto statement.

    Code:
    int i;
    
    i = 0;
    
    start_Loop:
    
    i+i+1;
    
    if(i<10) goto start_Loop;
    
    The idea behind what's call "syntactical sugar" is that pre-defined concepts are made into a unit and thus are easier to use.

    http://en.wikipedia.org/wiki/Syntactic_sugar
     
  5. kuwisdelu macrumors 65816

    Joined:
    Jan 13, 2008
    #5
    Another way of thinking about it is also: break; always has the same behavior, and other programmers know exactly what to expect when they see a break: "Okay, we're done with this loop." On the other hand, goto can become very convoluted, and while it's simple enough in a short program like this, imagine having to hunt around a big block of code for that goto label, trying to figure out where you're supposed to go and what the intended behavior is. It's possible to not use for or while loops at all —*just use goto. But that would result in code that's very difficult to understand.
     
  6. holmesf, Feb 28, 2011
    Last edited: Feb 28, 2011

    holmesf macrumors 6502a

    Joined:
    Sep 30, 2001
    #6
    The while condition of the loop is redundant. If it evaluates the condition, then the condition must be false, because otherwise it would have followed the goto statement and exited the loop already. It could actually be replaced with do { ... } while(1);

    Some advise about goto: never use it, not unless you have a damned good reason (like, say you're programming an operating system). If someone applied for a job and wrote that code it would certainly call into question whether or not to hire them.

    http://www.u.arizona.edu/~rubinson/copyright_violations/Go_To_Considered_Harmful.html
     
  7. dmi macrumors regular

    Joined:
    Dec 21, 2010
    #7
    It may make more sense to loop while denominator != 0
    since 0 % denominator is valid but numerator % 0 is not
     
  8. Bill McEnaney macrumors 6502

    Joined:
    Apr 29, 2010
    #8
    I agree.
     
  9. Bill McEnaney macrumors 6502

    Joined:
    Apr 29, 2010
    #9
    Correct me if I'm wrong. But I think Scott can go without both break and goto. The computer will thank the user when the loop finishes running.
     
  10. mydogisbox macrumors member

    Joined:
    Jan 16, 2011
    #10
    That actually isn't true (assuming he changes his "if (numerator == 0)" to "if (denominator == 0)"). To my understanding, mod 0 is undefined and thus he would need to break before trying to calculate the remainder if he wants to have a program that won't crash.
     
  11. balamw, Mar 1, 2011
    Last edited: Mar 1, 2011

    balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #11
    That case can still be handled properly without a goto or break. Here's a slight modification of the code that just adds a couple of ifs.

    It still bombs badly if you enter anything other than an integer due to scanf.

    Code:
    #include <stdio.h>
    
    int main (void)
    
    {
        int remainder, numerator, denominator ;
        printf ("This program computes the remainder of user entered numbers, \n" ) ;
        printf ("Enter 0 as dividend to exit\n\n") ;
        
        do {
            printf ("Enter a dividend: ") ;
            scanf ("%d", &numerator) ;
            if (numerator != 0) {
                printf ("Enter a divisor: ") ;
                scanf ("%d", &denominator) ;
                
                if (denominator != 0) {
                    remainder = numerator % denominator ;
                    printf ("\nRemainder: %d\n\n", remainder) ;
                }
                else 
                    printf("\nERROR: denominator cannot be zero\n");    
            }
        } while (numerator != 0); 
        
        printf ("Thank you, have a nice day\n") ;
        
        return 0;
        
    }
    
    The nice thing about this method is that you can use an editor that supports code folding to hide the paths you don't need to to see right now. The downside is that you get more levels of indent and a bit more overhead from the extra ifs.

    B
     
  12. mydogisbox macrumors member

    Joined:
    Jan 16, 2011
    #12
    If statements are more clear in this case. I'm not disputing if vs break, I was just trying to get away from goto statements at any cost. There are many more cases where I would use break than I would use goto. The particular post I was responding to was suggesting just removing the if statement which doesn't work.
     
  13. benlee macrumors 65816

    benlee

    Joined:
    Mar 4, 2007
  14. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #14
    I concur 100%. I will generally avoid goto/break/continue where possible unless their use is immediately understandable on a single screen of code.

    I was only providing an example that supports Bill's point. There is no need for break or goto in this loop.

    The nice thing about using the numerator/dividend as the loop exit condition is that it allows you to not ask for a denominator/divisor and skip lots of the code. However, you do need to validate all input. In this case that means not calculating mod for cases where it would be undefined.

    It's in his sig:

    B
     
  15. lee1210 macrumors 68040

    lee1210

    Joined:
    Jan 10, 2005
    Location:
    Dallas, TX
    #15
    I've been avoiding the threads that reference goto because it makes my blood boil. The only reason to ever know anything about goto is to be able to read someone else's code that uses it so you can remove it and replace it with a more sane control structure. Even in the best of times it is going to be jarring for someone to deal with it, and it ruins all assumptions about flow control, invariants, etc.

    http://xkcd.com/292/

    My initial inclination when these threads popped up was to say "Run away, skip that chapter!", but especially for continue and break, you will see these used. Hopefully you are lucky enough never to see someone use a goto, but you may as well know alternatives and how to use them effectively so you can refactor goto-laden code if you come across it.

    -Lee
     
  16. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #16
    It's just another example for me about how I don't quite like this book.

    Goto should be relegated to a chapter right at the end of the book that you can only get to by breaking a seal that says: "Here be dragons. Enter at your own peril".

    I see one of the dragons (or was that a dinosaur?) made it to the xkcd strip.

    B
     
  17. ulbador macrumors 68000

    ulbador

    Joined:
    Feb 11, 2010
    #17
    I honestly think that if you find yourself needing to use a goto, your code is already in a pretty much fundamentally broken. Take these cases:

    Code:
    //case 1
    int x = 2;
    if (x == 1) {
    	goto xIsValid;
    }
    
    if (x == 2) {
    	goto xIsValid;
    }
    
    printf("x is neither 1 or 2\n");
    
    xIsValid:
    	printf("x is 1 or 2\n");
    
    This case uses GOTO to basically skip over the "neither 1 or 2" line if x is not equal to one of those numbers. The immediate and obvious fault is if x is not 1 or 2, it will "fall through" and print out the "IS 1 or 2" line.


    Code:
    //case 2
    int x = 2
    do {
    	if (x == 1) {
    		break;
    	}
    	if (x == 2) {
    		break;
    	}
    	printf("x is neither 1 or 2");
    } while (false);
    
    printf("x is 1 or 2");
    
    This code uses a do/while control structure so that a break can be used in lieu of a GOTO. Obviously it suffers from the same problem. Bottom line, this code is basically broken because it relies on a sequential structure.


    Code:
    //Case 3
    void xIsValid(int x) {
    	printf("x is %d and valid",x);
    }
    
    int x = 2
    if (x == 1) {
    	xIsValid(x);
    } else if (x == 2) {
    	xIsValid(x);
    } else {
    	printf("x is neither 1 or 2");
    }
    
    This is rewritten to not require a break or a GOTO. Not that this is awesome code and not to say there is never a case when a break is required, but in many cases people just use these simple control structures to avoid having to write cleaner and modularized code.
     
  18. mydogisbox macrumors member

    Joined:
    Jan 16, 2011
    #18
    There are cases where I think goto is appropriate. When using a doubly nested loop to search through a data structure, I haven't found a cleaner way to exit the three loops than just a simple goto. The deeper the loop structure, the uglier the solution if you don't use a goto. If you know of a better way then please enlighten me. I've spent quite a bit of time trying to come up with something prettier.
    Code:
    for(int i = 0; i<10; i++)
    {
      for(int j = 0; j<10;j++)
      {
        for(int k = 0; k<10;k++)
        {
          if(condition == true /* peudo-code*/)goto exit_loops;
        }
      }
    }
    exit_loops:
    
    Other options:
    Code:
    for(int i = 0; i<10; i++)
    {
      for(int j = 0; j<10;j++)
      {
        for(int k = 0; k<10;k++)
        {
          if(condition == true /* peudo-code*/)
          {
             i = 10;
             j = 10;
             k = 10;
          }
        }
      }
    }
    
    Code:
    for(int i = 0; i<10; i++)
    {
      for(int j = 0; j<10;j++)
      {
        for(int k = 0; k<10;k++)
        {
          if(condition == true /* peudo-code*/)break;
        }
        if(condition == true /* peudo-code*/)break;
      }
      if(condition == true /* peudo-code*/)break;
    }
    
    Code:
    bool found = false;
    for(int i = 0; i<10 && !found; i++)
    {
      for(int j = 0; j<10 && !found;j++)
      {
        for(int k = 0; k<10 && !found;k++)
        {
          if(condition == true /* peudo-code*/)found = true;
        }
      }
    }
    
     
  19. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #19
    Your last one is more to my taste personally. I can look immediately at the for statements and know that they might not run through all the indices.

    Why do you find the goto cleaner?

    Personally, I would prefer to use while loops and manually initialize and increment the counters. It would be more verbose, but IMHO clearer since the exit condition would be explicit.

    B
     
  20. ulbador macrumors 68000

    ulbador

    Joined:
    Feb 11, 2010
    #20
    I maintain there has to be a better way to write this code. I don't think well planned and written code should actually ever even have a multi-level break. If you do actually need a structure like this, I would probably break down the "nests" into a series of functions instead.

    Given the methods you mention, I agree with balamw and using expressing the the break in the invariant.
     
  21. balamw, Mar 1, 2011
    Last edited: Mar 1, 2011

    balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #21
    Here's another alternative that gets rid of the nested loops and makes the purpose of the loop transparent:

    Code:
    bool found = false;
    long unsigned int count = 0;
    while (!found) {
      /* extract i, j, k indices from count and do stuff with them */ 
      /* condition now has to check that i, j and k are out of range as well as if the item was found*/
      if(condition == true) found = true; 
      count++;
    }
    
    I'm not saying I particularly like it, but it is an alternative that could work well in certain situations.

    EDIT: The extraction bit could be implemented as something like:
    Code:
    i = count/(10*10);
    j = (count/10)%10;
    k = count%10;
    
    for the case where i, j, k are 0-9.

    B
     
  22. Thethuthinang macrumors member

    Joined:
    Jan 3, 2011
    #22
    I'm glad someone brought this up. I'm new to programming, and although I completely understand that the use of goto is bad in some situations, I've been wondering why there is such avoidance of continue and break. I've read several articles about it. I'm wondering whether the avoidance is really on the level of superstition. Code with continue and break is easy to understand, so isn't the issue really whether it works well with computer architecture? The following is quoted from http://en.wikipedia.org/wiki/Structured_programming:

    This seems to make sense. But I wouldn't really know.
     
  23. balamw Moderator

    balamw

    Staff Member

    Joined:
    Aug 16, 2005
    Location:
    New England
    #23
    Code with continue and break can be easy to understand, but it can also be far more difficult than the alternatives depending on the specific situation.

    When the code is more complicated than the simple examples we are talking about here and several break/continue statements are buried in the body of the loop it can really confuse things.

    I've mentioned it already somewhat in the thread, but I find Code Folding extremely useful, and having alternative ways out of the loop decreases its functionality quite a bit.

    B
     
  24. Sydde macrumors 68020

    Sydde

    Joined:
    Aug 17, 2009
    #24
    I think the key point here is modularity and encapsulation. You have these control structures in C which are all governed by these "{", "}" things. What lives within those braces should be discreet, logically opaque and blind to what is outside them. The main reason for this is so that you can modify a section of code without affecting the flow of what lives outside its braces or encapsulated within it. As soon as you start using goto to cross encapsulation boundaries, you sacrifice the discreet modularity of your code, compromise its resilience increase its maintenance requirement.

    break and continue are problematic because they muddy the readability of you source. Alternative design is not at all difficult to come up with, and it becomes habit very quickly.
     
  25. milbournosphere macrumors 6502a

    milbournosphere

    Joined:
    Mar 3, 2009
    Location:
    San Diego, CA
    #25
    Please get rid of the goto. Use break instead. GOTO is useful in assembly code, but in a language like C, it just continues a practice that will lead to spaghetti code. Nip it in the bud, and you'll be thankful later.
     

Share This Page