8
#include <stdio.h>

int main() {
    while (height > 0) {
        if (throttle >= 0 && throttle <= 100) {
            printf("%d    %.1f  %.1f   %.1f  ", time, height, velocity, fuel);
            scanf("%d", &throttle);
            height = heightTemp + velocityTemp - (throttle * K-G) / 2;
            velocity = velocityTemp + (throttle * K-G);
            fuel = fuelTemp - throttle;
            time = time + 1;
            heightTemp = height;
            velocityTemp = velocity;
            fuelTemp = fuel;   
        }
        else {
            printf("Please choose a number between 0 and 100! \n");
            break;
        }
    }
   
    if (velocity >= -2.0) {
        printf("You landed successfully: ");
    } 
    else {
        printf("Failed! You crashed");
    }

    return 0;
}

I want to only run the if velocity part if the loop does not break, if I keep the code this way it'll run that code no matter what since the break obviously only quits the loop. My full code is not written.

  • 1
    A note: `throttle`, `time`, `height`, `velocity`, and `fuel` all come from nowhere. – Chris Sep 03 '22 at 17:14
  • There's basically two "solutions" to this: (1) use a `didBreak` variable that you set to 1 upon `break`ing, like in the answer below, or (2) use a `goto pastTheIfElse;` instead of `break`. If the `pastTheIfElse` label marks a `return` (like it does in your code), then you can do a `return` instead of the `goto`. – Petr Skocik Sep 03 '22 at 17:16
  • I was always told to avoid goto if at all possible as it can lead to spagetti code – Paul Hashmi Sep 03 '22 at 17:19
  • 5
    @PaulHashmi Telling students to avoid something at all costs, means that the prof is not able to teach how to write well structured code or that the prof has little faith in the students.Programming is structuring code and data in an efficient way and after decades of programming (aka computer science) it should be possible to teach that properly. `goto` is a valid statement in the C language, use it if it helps, don't use it, if it makes it worse. Remember the hammer and nail analogy? – Erdal Küçük Sep 03 '22 at 17:36
  • 2
    I didnt say at all costs, I said if at all possible. I agree with you completely however following goto all over the place can become a nightmare. Everyone does things there own way but I think in this case just a flag would do. Opinion not fact, each to there own. – Paul Hashmi Sep 03 '22 at 17:40
  • 1
    @PaulHashmi Ok, sorry, you didn't say that, indeed. – Erdal Küçük Sep 03 '22 at 17:48
  • @Chris yes had to remove a lot of my code because it wouldn't post. On my real code everything is added and the code makes more sense. Since the question was only aimed at a specific area the rest of the code wasn't needed either way. – Mods told me to change name Sep 03 '22 at 18:33
  • Note that your question would be of higher quality if you posted a [mre] of the problem which actually compiles. That way, it would be possible for people answering your question to test their solutions and to demonstrate them. – Andreas Wenzel Sep 04 '22 at 01:56
  • Thank you for guiding me to that page, I’ll make sure to follow it for the next question. – Mods told me to change name Sep 04 '22 at 07:23

5 Answers5

6

There are basically two solutions to this:

  1. use a idBreak variable that you set to 1 upon breaking, like in the mikyll98's answer
  2. use a goto pastTheIfElse; instead of break;.

If the pastTheIfElse label marks a return (like it does in your code), then you can directly return instead of the goto.

#include <stdio.h>

int main() {
    while (height > 0) {
        if (throttle >= 0 && throttle <= 100) {
            printf("%d    %.1f  %.1f   %.1f  ", time, height, velocity, fuel);
            scanf("%d", &throttle);
            height = heightTemp + velocityTemp - (throttle * K-G) / 2;
            velocity = velocityTemp + (throttle * K-G);
            fuel = fuelTemp - throttle;
            time = time + 1;
            heightTemp = height;
            velocityTemp = velocity;
            fuelTemp = fuel;   
        }
        else {
            printf("Please choose a number between 0 and 100! \n");
            goto pastTheIfElse; //instead of break;
            //OR in this case: `return 0;`
        }
    }

    if (velocity >= -2.0) {
        printf("You landed successfully: ");
    } else {
        printf("Failed! You crashed");
    }
    pastTheIfElse:

    return 0;
}

The goto solution is what an optimizing compiler should optimize the didBreak version into, so some prefer it, while others are really really strongly against it because "goto considered harmful".

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
  • 1
    This option seems easier to both read and its shorter code what I’m not quite understanding is why some people don’t like this method what are the disadvantages of writing it like this. – Mods told me to change name Sep 03 '22 at 19:28
6

Replace the break; with return 0; or if you consider invalid input an error of value to the host environment return -1; (or other non-zero value - it is implementation defined).

If you want to be ultra-formal #include <stdlib.h> and return EXIT_SUCCESS or return EXIT_FAILURE as you see applicable. Though use of those provided macro constants are rarely seen in either examples of real code.

Some C Standards hold functions should have only one exit point but obeying that sometimes leads to code that is just as contorted and unreadable as an early return where relevant.

Persixty
  • 8,165
  • 2
  • 13
  • 35
  • [Some older POSIX functions only support return values in the range `0` to `255`.](https://stackoverflow.com/a/5149399/12149471) Therefore, I believe `1` would be a better return value than `-1`. – Andreas Wenzel Sep 04 '22 at 00:31
  • @AndreasWenzel Formally only `EXIT_SUCCESS` and `EXIT_FAILURE' and `0` are defined and `EXIT_SUCCESS` may something other than zero. I've worked with environments where negative is bad returns because an actual results are positive. It's open season outside the standard. – Persixty Sep 04 '22 at 00:42
4

Just assign a certain value to a variable if the loop breaks, and add an if condition that checks that value.

#include <stdio.h>

int main() {
    int didBreak = 0;

    while (height > 0)
    {
        if (throttle >= 0 && throttle <= 100)
        {
            printf("%d    %.1f  %.1f   %.1f  ", time, height, velocity, fuel);
            scanf("%d", &throttle);
            height = heightTemp + velocityTemp - (throttle * K - G) / 2;
            velocity = velocityTemp + (throttle * K - G);
            fuel = fuelTemp - throttle;
            time = time + 1;
            heightTemp = height;
            velocityTemp = velocity;
            fuelTemp = fuel;

        }
        else
        {
            printf("Please choose a number between 0 and 100! \n");
            didBreak = 1;
            break;
        }
    }

    if (didBreak == 0)
    {
        if (velocity >= -2.0)
        {
            printf("You landed successfully: ");
        }
        else
        {
            printf("Failed! You crashed");
        }
    }

    return 0;
}
Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
mikyll98
  • 1,195
  • 3
  • 8
  • 29
  • You might lose a level of indentation by just having `if (didBreak) return 0;` – Chris Sep 03 '22 at 17:13
  • 1
    @Chris you're right and I agree, but I wrote it that way so the OP (which to me seems to be still learning the fundamentals) could better understand its meaning :) – mikyll98 Sep 03 '22 at 17:17
  • 1
    I can appreciate that. I find that newcomers to C (among many other languages) often end up lost in a sea of indentation. I know that even though I know what I'm doing I start to get lost at around 4-5 levels. – Chris Sep 03 '22 at 17:19
  • 4
    Cheers this helped me a lot and was very easy to understand aswell. – Mods told me to change name Sep 03 '22 at 17:19
4

In my opinion, the best solution to your stated problem would be to replace break with return 1;, so that it exits the function. If you #include <stdlib.h>, then you can also use return EXIT_FAILURE; instead, which is easier to read.

However, in cases in which you do not want to exit the function (for example because you want the rest of the function to be executed), then I would recommend that you restructure your loop to an infinite loop with explicit break statements for all possible cases in which the loop should end. That way, you can move the if...else inside the loop:

for (;;) //infinite loop, equivalent to while(true)
{
    if ( height <= 0 )
    {
        if ( velocity >= -2.0 )
        {
            printf( "You landed successfully.\n" );
        }
        else
        {
            printf( "Failed! You crashed!\n" );
        }

        break;
    }

    if ( throttle >= 0 && throttle <= 100 )
    {
        [...]
    }
    else
    {
        printf( "Please choose a number between 0 and 100!\n" );
        break;
    }
}

However, I do not think that your stated problem is your actual problem. When the user enters invalid input, you probably do not want to exit the function (or even the entire program). Instead, it would make more sense to reprompt the user for valid input, when the input is invalid. In order to determine whether the input is invalid or not, you should not only check whether throttle is in the desired range, but also whether the user entered an integer in the first place (i.e. make sure that the user didn't enter something that could not be converted to an integer).

For the reasons stated in this answer of mine to another question, I do not recommend that you use scanf for line-based user input. For determining whether the user entered a valid integer, it is usually better to use the function fgets to read exactly one line of user input as a string, and then to attempt to convert the string to an integer using the function strtol. These two function calls can be combined into a single function, which automatically reprompts the user if he did not enter a valid integer. In my linked answer, I created such a function called get_int_from_user.

After making the changes mentioned above, your code would look like this:

for (;;) //infinite loop, equivalent to while(true)
{
    printf( "T: %d   H: %.1f   V: %.1f   F: %.1f\n", time, height, velocity, fuel );

    if ( height <= 0 )
    {
        if ( velocity >= -2.0 )
        {
            printf( "You landed successfully.\n" );
        }
        else
        {
            printf( "Failed! You crashed!\n" );
        }

        break;
    }

    //repeat until user entered a valid integer in the
    //range 0 - 100
    for (;;)
    {
        throttle = get_int_from_user( "Enter throttle (0-100): " );
        if ( throttle < 0 || throttle > 100 )
        {
            printf( "Please choose a number between 0 and 100!\n" );
            continue;
        }

        //input is ok
        break;
    }

    //do physics calculations (copied from the question)
    height = heightTemp + velocityTemp - (throttle * K-G) / 2;
    velocity = velocityTemp + (throttle * K-G);
    fuel = fuelTemp - throttle;
    time = time + 1;
    heightTemp = height;
    velocityTemp = velocity;
    fuelTemp = fuel;  
}

Here is the full code of a test program:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>

int get_int_from_user( const char *prompt );

int main( void )
{
    int time = 0, throttle;
    double height = 100.0, velocity = -5.0;

    for (;;) //infinite loop, equivalent to while(true)
    {
        printf( "T: %d   H: %.1f   V: %.1f\n", time, height, velocity );

        if ( height <= 0 )
        {
            if ( velocity >= -2.0 )
            {
                printf( "You landed successfully.\n" );
            }
            else
            {
                printf( "Failed! You crashed!\n" );
            }

            break;
        }

        //repeat until user entered a valid integer in the
        //range 0 - 100
        for (;;)
        {
            throttle = get_int_from_user( "Enter throttle (0-100): " );
            if ( throttle < 0 || throttle > 100 )
            {
                printf( "Please choose a number between 0 and 100!\n" );
                continue;
            }

            //input is ok
            break;
        }

        //do physics calculations
        velocity += throttle / 100.0;
        velocity -= 0.5; //gravity
        height += velocity;
        time++;
    }
}

int get_int_from_user( const char *prompt )
{
    //loop forever until user enters a valid number
    for (;;)
    {
        char buffer[1024], *p;
        long l;

        //prompt user for input
        fputs( prompt, stdout );

        //get one line of input from input stream
        if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
        {
            fprintf( stderr, "Unrecoverable input error!\n" );
            exit( EXIT_FAILURE );
        }

        //make sure that entire line was read in (i.e. that
        //the buffer was not too small)
        if ( strchr( buffer, '\n' ) == NULL && !feof( stdin ) )
        {
            int c;

            printf( "Line input was too long!\n" );

            //discard remainder of line
            do
            {
                c = getchar();

                if ( c == EOF )
                {
                    fprintf( stderr, "Unrecoverable error reading from input!\n" );
                    exit( EXIT_FAILURE );
                }

            } while ( c != '\n' );

            continue;
        }

        //attempt to convert string to number
        errno = 0;
        l = strtol( buffer, &p, 10 );
        if ( p == buffer )
        {
            printf( "Error converting string to number!\n" );
            continue;
        }

        //make sure that number is representable as an "int"
        if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
        {
            printf( "Number out of range error!\n" );
            continue;
        }

        //make sure that remainder of line contains only whitespace,
        //so that input such as "6sdfj23jlj" gets rejected
        for ( ; *p != '\0'; p++ )
        {
            if ( !isspace( (unsigned char)*p ) )
            {
                printf( "Unexpected input encountered!\n" );

                //cannot use `continue` here, because that would go to
                //the next iteration of the innermost loop, but we
                //want to go to the next iteration of the outer loop
                goto continue_outer_loop;
            }
        }

        return l;

    continue_outer_loop:
        continue;
    }
}

Since the physics calculations that you posted do not compile, I have added my own (simple) physics calculations.

This program has the following output:

T: 0   H: 100.0   V: -5.0
Enter throttle (0-100): hh7
Error converting string to number!
Enter throttle (0-100): test
Error converting string to number!
Enter throttle (0-100): 6abc
Unexpected input encountered!
Enter throttle (0-100): 200
Please choose a number between 0 and 100!
Enter throttle (0-100): -30
Please choose a number between 0 and 100!
Enter throttle (0-100): 5 
T: 1   H: 94.5   V: -5.5
Enter throttle (0-100): 10
T: 2   H: 88.7   V: -5.9
Enter throttle (0-100): 15
T: 3   H: 82.5   V: -6.2
Enter throttle (0-100): 20
T: 4   H: 76.0   V: -6.5
Enter throttle (0-100): 25
T: 5   H: 69.2   V: -6.8
Enter throttle (0-100): 35
T: 6   H: 62.4   V: -6.9
Enter throttle (0-100): 45
T: 7   H: 55.4   V: -7.0
Enter throttle (0-100): 55
T: 8   H: 48.5   V: -6.9
Enter throttle (0-100): 65
T: 9   H: 41.8   V: -6.8
Enter throttle (0-100): 80
T: 10   H: 35.3   V: -6.5
Enter throttle (0-100): 100
T: 11   H: 29.3   V: -6.0
Enter throttle (0-100): 100
T: 12   H: 23.9   V: -5.5
Enter throttle (0-100): 100
T: 13   H: 18.9   V: -5.0
Enter throttle (0-100): 100
T: 14   H: 14.5   V: -4.5
Enter throttle (0-100): 100
T: 15   H: 10.6   V: -4.0
Enter throttle (0-100): 100
T: 16   H: 7.1   V: -3.5
Enter throttle (0-100): 100
T: 17   H: 4.2   V: -3.0
Enter throttle (0-100): 100
T: 18   H: 1.7   V: -2.5
Enter throttle (0-100): 100
T: 19   H: -0.2   V: -2.0
You landed successfully.
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • Hey Andreas and thank you for such a good recommendation, I know that there are ways to make the code better, the same way you have done by acknowledning that the user can input other stuff than int. However in my course that I’m currently taking we haven’t gone as far as your current code. Even though I completely understand how it works. I wouldnt wanna rush through and skip steps in my course. – Mods told me to change name Sep 04 '22 at 07:21
3

It doesn't make sense to prompt for re-entry of data, then exit the processing and go off somewhere else...

Revamp your while loop and test user input when it is input.

Perhaps a "sentinel value" (eg: 999) could be available to abort the loop...

while( height > 0 ) {
    printf("%d    %.1f  %.1f   %.1f  ", time, height, velocity, fuel);
    scanf("%d", &throttle);
    if( throttle < 0 || throttle > 100 ) {
        if( throttle == 999 ) return -1; // Maybe???
        printf("Please choose a number between 0 and 100! (999 to abort) \n");
        continue;
    }

    /* Calculations omitted */
}
Fe2O3
  • 6,077
  • 2
  • 4
  • 20