0

I'm trying to read user integer input with the following code. I'm aware that strtol returns a long value.

long color = 1;
char buf[256];
char * last;

while(color != 0){
    printf("Enter a color (0 to stop): ");
    fgets(buf, 256, stdin);

    color = strtol(buf, &last, 10);

    if(last != '\0'){
        printf("You didn't enter a number.\n");
    }
    else{
        printf("%d\n", color);
    }
}

I tried running this, first entering 4, which gave me You didn't enter a number. I tried it again with "forty" and got the same result. What am I missing here? All I want to do is get basic user input for an integer value that verifies only an integer was entered. I don't need a long because the values will all be between 3000 and 4000.

user3308219
  • 157
  • 2
  • 11

3 Answers3

0

Change that if(last != '\0') to if( *last != '\0'). You want to check what last points to

From https://linux.die.net/man/3/strtol

If endptr is not NULL, strtol() stores the address of the first invalid character in *endptr. If there were no digits at all, strtol() stores the original value of nptr in *endptr (and returns 0).

In particular, if *nptr is not '\0' but **endptr is '\0' on return, the entire string is valid.

Community
  • 1
  • 1
Mawg says reinstate Monica
  • 38,334
  • 103
  • 306
  • 551
0

last != '\0' is equivalent to last != NULL. You're testing whether last is a null pointer, which it never is, because strtol never sets *endptr to NULL.

You should probably do this instead:

if (last == buf || *last != '\0')

(The first condition handles the case where buf is an empty string.)

There's a secondary problem in your input code:

fgets(buf, 256, stdin);

This line does not check the return value of fgets to make sure buf was filled, and it does not remove the trailing newline of any user input.

Do this instead:

if (!fgets(buf, 256, stdin)) {
    break;
}
buf[strcspn(buf, "\n")] = '\0';

As for why '\0' acts as a null pointer here:

Any integer constant expression with value zero is implicitly converted to a null pointer if used in a pointer context. '\0' is such a zero-valued integer expression, and you're comparing it to a pointer, so it is in pointer context.

melpomene
  • 84,125
  • 8
  • 85
  • 148
0

For OP's specific case, it is sufficient to test for extra non-numeric text after the number.

char buf[256];

if (fgets(buf, sizeof buf, stdin)) Handle_EndOfFile();
char *last;
long color = strtol(buf, &last, 10);

while (isspace(*last)) last++; // tolerate trailing white-space like '\n'

// Do not compare the pointer, compare what it points to
// if(last != '\0'){
if (*last || color < 3000 || color > 4000) {
  Handle_NonNumeric("You didn't enter a number in 3000-4000 range.\n");
} else {
  foo(color); // Success
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256