18
\$\begingroup\$

This was a homework assignment that I'm now done with - I submitted it as is. However the fact that I needed to use the same code twice bugged me... The double code is:

printf("Enter a distance in inches (0 to quit): ");
scanf("%f",&input);

Is there a better way to do the same thing in my loop instead of the double scanf/printf? It does need to quit the program immediately if a 0 is entered.

#include <stdio.h>
int main()
{
    float distance, floatFeet, input;
    int feet;

    printf("Enter a distance in inches (0 to quit): ");
    scanf("%f", &input);
    while (input != 0)
    {
        feet = input/12;
        distance = (input-feet*12);
        floatFeet = input/12;
        printf("%d feet and %f inches or %f feet \n\n", feet, distance, floatFeet);
        printf("Enter a distance in inches (0 to quit): ");
        scanf("%f", &input);
    }
}
\$\endgroup\$
3
  • 13
    \$\begingroup\$ there is a codereview.SO where questions like are better suited \$\endgroup\$
    – ratchet freak
    Feb 21, 2013 at 14:29
  • 1
    \$\begingroup\$ And off we go... \$\endgroup\$
    – Michael Myers
    Feb 22, 2013 at 15:41
  • \$\begingroup\$ I would flush the output before reading for the input.. \$\endgroup\$
    – renoX
    Feb 26, 2013 at 9:10

7 Answers 7

19
\$\begingroup\$

In general, you would need to insert an explicit conditional somewhere in order to distinguish between the first iteration (when no results should be displayed before the prompt) and any subsequent ones. That's because the "do not display output" condition is different from the "exit program" condition, so you can't just shove both into the loop condition.

Since you are going to insert an if no matter what, you might as well make it break out of the loop and make the loop infinite:

while (1)
{
    printf("Enter a distance in inches (0 to quit): ");
    scanf("%f",&input);

    if (input == 0) break;

    feet = input/12;
    inches = (input-feet*12);
    floatFeet = input/12;       
    printf("%d feet and %f inches or %f feet \n\n",feet,inches,floatFeet);
}
\$\endgroup\$
5
  • 2
    \$\begingroup\$ In C, you'd probably want while(1), not while(true). \$\endgroup\$
    – Antimony
    Feb 21, 2013 at 13:52
  • 3
    \$\begingroup\$ @Antimony: So true, thanks (no pun intended). I 've blown my cover! ;-) \$\endgroup\$
    – Jon
    Feb 21, 2013 at 13:53
  • \$\begingroup\$ while (true) throws an 'undeclared' error at me, should I just use while (1)? \$\endgroup\$
    – Symon
    Feb 21, 2013 at 13:57
  • \$\begingroup\$ ah okay thanks antimony, comments didn't refresh right away :P \$\endgroup\$
    – Symon
    Feb 21, 2013 at 13:57
  • 1
    \$\begingroup\$ In C, the idiom for "infinite loop" is for(;;) { .... } \$\endgroup\$
    – vonbrand
    Feb 22, 2013 at 1:56
30
\$\begingroup\$

You could move the duplicated lines into a function, something like this should work:

#include <stdio.h>

float get_input(void)
{
    float input;

    printf("Enter a distance in inches (0 to quit): ");
    scanf("%f", &input);

    return input;
}

int main()
{
    float inches, floatFeet, input;
    int feet;

    while (input = get_input())
    {
        feet = input/12;
        inches = (input-feet*12);
        floatFeet = input/12;
        printf("%d feet and %f inches or %f feet \n\n",feet,inches,floatFeet);
    }

    printf("Goodbye!\n");
}
\$\endgroup\$
13
  • 1
    \$\begingroup\$ @mskfisher, thanks! Maybe out of scope though, if the OP has not learned about functions and such yet. \$\endgroup\$
    – harald
    Feb 21, 2013 at 14:37
  • 1
    \$\begingroup\$ @Marton while(input = get_input()) checks whether input is 0. \$\endgroup\$ Feb 21, 2013 at 14:43
  • 1
    \$\begingroup\$ @Marton It's not while(0), it's while(input) - except that input also gets assigned when the loop condition is evaluated. \$\endgroup\$ Feb 21, 2013 at 14:56
  • 1
    \$\begingroup\$ @marco-fiset This is possible in C99 but not in classic C. C99 doesn't really seem to have set off yet \$\endgroup\$ Feb 21, 2013 at 15:13
  • 1
    \$\begingroup\$ Better would be while ((input = get_input()) != 0) which won't give a compiler warning and doesn't confuse the assignment with a boolean. \$\endgroup\$ Feb 22, 2013 at 16:31
10
\$\begingroup\$

You can use a do while loop, which will always execute once and check the condition at the end of the loop.

If you need the program to exit immediately without printing when a 0 is entered, you could just put the calculation and printing code in an if statement within the loop:

do {
    printf("Enter a distance in inches (0 to quit): ");
    scanf("%f",&input);
    if( input != 0 ) {
        feet = input/12;
        inches = (input-feet*12);
        floatFeet = input/12;       
        printf("%d feet and %f inches or %f feet \n\n",feet,inches,floatFeet);
    }    
} while (input != 0)

Or, to avoid repeating anything:

while(true) {
    printf("Enter a distance in inches (0 to quit): ");
    scanf("%f",&input);
    if( input == 0 ) {
         break;
    }
    feet = input/12;
    inches = (input-feet*12);
    floatFeet = input/12;       
    printf("%d feet and %f inches or %f feet \n\n",feet,inches,floatFeet);
}
\$\endgroup\$
0
7
\$\begingroup\$

You can replace this loop with a "forever" loop that exits from the middle of its body, like this:

for (;;) {
    printf("Enter a distance in inches (0 to quit): ");
    scanf("%f",&input);
    if (input == 0) break;
    feet = input/12;
    inches = (input-feet*12);
    floatFeet = input/12;       
    printf("%d feet and %f inches or %f feet \n\n",feet,inches,floatFeet);
}

There are multiple ways to do a "forever" loop; this one is borrowed from K&R's book.

\$\endgroup\$
5
  • \$\begingroup\$ Any reason to use a for loop instead of a while loop when making a 'forever' loop? \$\endgroup\$
    – Symon
    Feb 21, 2013 at 13:55
  • 3
    \$\begingroup\$ @Symon I use for(;;) because it is a very common idiom popularized by "the" book on the C language. Other than that, there is no reason to go one way or the other: the compiler will generate identical binary code for both loops. \$\endgroup\$ Feb 21, 2013 at 13:57
  • 3
    \$\begingroup\$ Some people even define: #define ever (;;) \$\endgroup\$
    – mouviciel
    Feb 21, 2013 at 13:58
  • 7
    \$\begingroup\$ @mouviciel: People who have just not learned to leave the preprocessor alone if at all possible. \$\endgroup\$
    – Jon
    Feb 21, 2013 at 13:59
  • 2
    \$\begingroup\$ @Symon In a compiler which does not optimize at all, while (1) might generate a comparison to a constant which is omitted in for (;;). However, all normal compilers would optimize that comparison away. \$\endgroup\$ Feb 21, 2013 at 15:06
5
\$\begingroup\$

Use do-while instead while.

#include <stdio.h>
int main()
{
    float inches, floatFeet, input;
    int feet;

    do
    {
        printf("Enter a distance in inches (0 to quit): ");
        scanf("%f",&input);
        if (input == 0) break;
        feet = input/12;
        inches = (input-feet*12);
        floatFeet = input/12;       
        printf("%d feet and %f inches or %f feet \n\n",feet,inches,floatFeet);      
    }
    while (input != 0);
}
\$\endgroup\$
6
  • \$\begingroup\$ "It does need to quit the program immediately if a 0 is entered." This would print "0 feet and 0 inches or 0 feet". \$\endgroup\$
    – lc.
    Feb 21, 2013 at 13:49
  • \$\begingroup\$ That depends on what you mean by immediately. Since the code executed before quitting has no side effects, it shouldn't matter. \$\endgroup\$
    – Antimony
    Feb 21, 2013 at 13:50
  • 1
    \$\begingroup\$ @Antimony The side effect is printing a line. I don't think that "shouldn't matter"...the code as edited will work though (although there is an extra comparison every time the loop loops). \$\endgroup\$
    – lc.
    Feb 21, 2013 at 13:50
  • 4
    \$\begingroup\$ Also, if you're breaking out explicitly, there's no point in a do loop. Just make it while(1). \$\endgroup\$
    – Antimony
    Feb 21, 2013 at 13:51
  • 1
    \$\begingroup\$ @Antimony The two methods may be functionally equivalent; but the do-while construct makes it explicit that it's not intended to be an infinite loop. That said, I'd probably replace the if (input == 0) break; construct with if(input !=0) { ...}and put the rest of the loop inside the {}'s to eliminate the second exit point from the loop as well. \$\endgroup\$ Feb 21, 2013 at 14:05
1
\$\begingroup\$

This is exactly where you use the:

do {
    //stuff
} while (condition)

... where you need to do something once, at least, but conditionally repeat the same task...

\$\endgroup\$
0
\$\begingroup\$

Symon, here is another alternative. Notice the following things:

  1. I have separated getting input into a function, get_input. The function takes a pointer to a variable to accept the input value and checks whether scanf actually reads a value from the user input. It returns an integer value that is non-zero (true) if scanf really did read a value. The exit condition has changed to ctrl-d which is the standard UNIX way of indicating end-of-input. Equally, typing a non-numeric will also cause scanf to return 0.

  2. The while loop now exits on failure of the input function.

  3. The variables are defined at the point of use. This is better practice as it reduces the scope of the variable (ie. the amount of the code where it is valid).

  4. distance is renamed inches, which seems more appropriate.

  5. The division is done only once and there is a cast (int) to avoid a conversion compiler warning (turn many warnings ON in your compiler).

  6. A new-line is printed on exit to be nice to the user.

    #include <stdio.h>
    
    static inline int get_input(float *input)
    {
        printf("\nEnter a distance in inches (ctrl-d to quit): ");
        return scanf("%f", input) == 1;
    }
    
    int main(void)
    {
        float input;
    
        while (get_input(&input) != 0) {
    
            float float_feet = input / 12;
            int feet = (int) float_feet;
            float inches = input - (feet * 12);
    
            printf("%d feet, %f inches or %f feet \n", feet, inches, float_feet);
        }
        putchar('\n');
        return 0;
    }
    
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy