5
\$\begingroup\$

Here is my attempt:

#include <stdio.h>

int main()
{
    int number = -1;
    printf("Enter a number in range [2,20]\n");
    scanf("%d", &number);
    while (number < 2 || 20 < number)
    {
        while (getchar() != '\n')
        {
        }
        printf("Try again\n");
        scanf("%d", &number);
    }
    printf("Your number was: %d", number);
    return 0;
}

But the solution from the professor is the following:

#include<stdio.h>

int main(){
    int n;
    while(scanf("%d",&n)==0 || n <2 || n >20){
        while ( getchar () != ’\n’);
        printf( "Wrong. Try again\n");
    }
    printf ( "%n\n",n);
    return 0;
}

Both approaches seem to work fine but is there a benefit to his method? Is it possible that scanf changes variable even though it couldn't read in a correct number. In that case checking the result of the scanf function would protect the program from accepting a wrong answer.

\$\endgroup\$
2
  • \$\begingroup\$ Edited to remove errors that I put in the solution code while pasting from a pdf and translating to English. \$\endgroup\$ – Zwarte Kop Dec 21 '19 at 16:40
  • 1
    \$\begingroup\$ Please do not update the code in your question to incorporate feedback from answers, doing so goes against the Question + Answer style of Code Review. This is not a forum where you should keep the most updated version in your question. Please see what you may and may not do after receiving answers. \$\endgroup\$ – Vogel612 Dec 21 '19 at 16:58
12
\$\begingroup\$

Prof's transcribed code has weaknesses

#include<stdio.h>

int main(){
    int n;
    while(scanf("%d",&n)==0 || n <2 || n >20){
        while ( getchar () != ’\n’);
        printf( "Wrong. Try again\n");
    }
    printf ( "%n\n",n);
    return 0;
}

Infinity loop

When input is closed after a scanf("%d",&n) == 0, getchar() can eventual return EOF before '\n' and thus while ( getchar () != ’\n’); never ends.

Smart quotes

’\n’ vs. '\n'. Transcription problem or is that in the original? In any case, the posted code was certainly not compiled.

Invalid specifier

"%n" does not print the int value. It is undefined behavior as "%n" matches an int *.

    //printf ( "%n\n",n);
    printf ( "%d\n",n);

Requirement weakness

"Ask the user a number between 2 and 20" is a classic ambiguity. Depending on perspective are 2 and 20 acceptable or not? Better to provide more detail

"Ask the user a number, inclusively, between 2 and 20" or the like.


OP code has issues too.

#include <stdio.h>

int main()
{
    int number = -1;
    printf("Enter a number in range [2,20]\n");
    scanf("%d", &number);
    while (number < 2 || 20 < number)
    {
        while (getchar() != '\n')
        {
        }
        printf("Try again\n");
        scanf("%d", &number);
    }
    printf("Your number was: %d", number);
    return 0;
}

Failure to check input return value

scanf("%d", &number); may have failed due to non-numeric text input. Check the result.

// scanf("%d", &number);
if (scanf("%d", &number) == 1) Success();
else Fail();

Infinite loop

OP's code suffers same potential infinite loop on end-of-file

// while (getchar() != '\n') { }
int ch;
while ((ch = getchar()) != '\n' && ch != EOF) { }

Also check scanf() return value.

Functional difference

With non-numeric input, Prof's code tries again. OP's code, which does not evaluate the return value of scanf(), technically leads to trouble on rare input error. On input error, the value of number is indeterminate. See comment

The 2 locations to take user input is simply twice the amount of code that needs checking. Better to take input from 1 call. Production code would have a get_int() to handle this. Something like getint()


but is there a benefit to his method?

OP's' code does have an informative prompt.

Else not much, both need work. Perhaps

// untested code
#include<stdio.h>
#define MIN_N 2
#define MAX_N 20

int main(void) {
  int cnt;
  int n;
  printf("Enter a number in range [%d,%d]\n", MIN_N, MAX_N);
  while((cnt = scanf("%d",&n)) == 0 || ((cnt == 1) && (n < MIN_N || n > MAX_N))) {
    int ch;
    while ((ch = getchar ()) != '\n' && ch != EOF) {
      ;
    }
    printf( "Wrong input. Try again\n");
  }
  if (cnt == 1) printf ( "%d\n",n);
  return 0;
}

Better code would read a line into a string and process the input with strtol().

\$\endgroup\$
3
  • 1
    \$\begingroup\$ Interesting, I read elsewhere that under no circumstances scanf() will change the variable when it fails. Apparently that's false. The invalid specifier and quotes errors come from the fact that this is copied from a dutch pdf that I had to translate to English. My bad. Thank you for your answer. \$\endgroup\$ – Zwarte Kop Dec 21 '19 at 16:38
  • \$\begingroup\$ @ZwarteKop Re: state of number on failure, on review the C spec is not so informative. %s and %[, an indeterminate state, at best, must the case. An input error may occur at anytime. To provide an unchanged buffer on error obliges a temp buffer reading in a string & then transferring to destination on no-error. For numeric targets, certainly input error could be detected before partial assignment - C spec is quiet on this. With fgets(), the spec is clear, on error, buffer is indeterminate and I apply this model to scanf(). Best to not rely on prior setting when input errors occurs. \$\endgroup\$ – chux - Reinstate Monica Dec 21 '19 at 18:07
  • 1
    \$\begingroup\$ @ZwarteKop Re "I read elsewhere that under no circumstances scanf() will change" Please forward the source citation so I may review. \$\endgroup\$ – chux - Reinstate Monica Dec 21 '19 at 23:27
0
\$\begingroup\$

There's actually, a special loop in C called the do...while loop to solve such questions:

Here you go:

#include<stdio.h>
#include<stdlib.h>

main(argc,argv)
const char** argv;
{
    int num=0;
    do {
        scanf("%d",&num);
    } while(num<2 || num>20);
    printf("The number is: %d",num);
    return 0;
}
\$\endgroup\$
-1
\$\begingroup\$

The one plus on the prof's code is that it has only one call to scanf(). Not repeating code when unneeded is good.

The minus on both implementations is the use of scanf() at all. Personally, I would use fgets() and strtol(), and perhaps ensured that the user hasn't typed something like "3 or 4".

\$\endgroup\$

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.