0

I am using gcc compiler on Linux Ubuntu. Here is my question: I am confused why my program does this. Why does it skip the rest of my scanf statements when I put a float. I know you do not want to put a float into an integer but why does it do that. Does not it chop off the decimal and why does it put big numbers in the variables. Can someone explain, please? P.S I know how to fix it, I just want to know why it does that.

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

int main() {
int i = 0;
int gamesPlayed = 0;
int goals[gamesPlayed];
int totalGoals = 0;
float avg = 0;

printf("How many games have you played? ");
scanf(" %d", &gamesPlayed);

for (i=0; i < gamesPlayed; i++)
{
    printf("How many goals did you score in game %d? ", i+1);
    scanf(" %d", &goals[i]);
    totalGoals += goals[i];
    printf("%d\n", totalGoals);
}

avg = ((float)totalGoals / gamesPlayed);
printf("Total Goals: %d\n", totalGoals);
printf("Avg: %.2f\n", avg);


return 0;
}   

Error:
How many games have you played? 2.6 2.000000

How many goals did you score in game 1? 1863382920

How many goals did you score in game 2? 1863415686

Total Goals: 1863415686

Avg: 931707840.00

Leos313
  • 5,152
  • 6
  • 40
  • 69
  • 2
    Quick answer: don't use `scanf` for user input. Use `fgets` and parse the input string yourself. – Jabberwocky Jan 22 '19 at 12:51
  • 3
    You never check the return value from `scanf()`. It's there for a reason. – Andrew Henle Jan 22 '19 at 12:52
  • 2
    Check the return value from `scanf` and take appropriate action if it cannot scan in the input – Ed Heal Jan 22 '19 at 12:52
  • 1
    @Jabberwocky Writing your own parser will not make it automatically error prone. I'd bet on proper use of `scanf` which is already tested and well documented. – montonero Jan 22 '19 at 12:59
  • 1
    @montonero You just need to know and handle everything described here http://sekrit.de/webdocs/c/beginners-guide-away-from-scanf.html – Yunnosch Jan 22 '19 at 13:00
  • 1
    @Yunnosch That's a funny reading. I could wrote the same article about "Do not use pointers". – montonero Jan 22 '19 at 13:05
  • @montonero parsing could be done easily with `sscanf`. – Jabberwocky Jan 22 '19 at 13:07
  • 1
    @Jabberwocky it could be for sure. But I wouldn't restrict use of `scanf` just because it could be misused. There's a lot things that could be misused in C. – montonero Jan 22 '19 at 13:09
  • https://stackoverflow.com/questions/26583717/how-to-scanf-only-integer is more or less a duplicate of this question. – Jabberwocky Jan 22 '19 at 13:31

2 Answers2

3

With the input 2.6 the first scanf will read the 2 and leave .6 in the input stream.

The next scanf will see the . which can't be converted to an integer. Consequently scanf returns and leaves goals[i] uninitialzed. The input stream will still contain .6 so exactly the same happens again and again in the loop.

Then you use the uninitialized variable(s) to calculate the average and therefore you end up with "strange" values. Note: Using uninitialized variables is undefined behavior.

Notice that the problem has nothing to do with float. The problem is simply that the input contains a character that can't be converted to an integer. Input like 2HelloWorld6 will give you the same result.

You need check the value returned by scanf like:

if (scanf(" %d", &goals[i]) != 1)
{
    // Ups - could not scan an integer

    ... add error handling here ...
}

BTW: Consider reading user input with fgets and do the scan with sscanf. It is in most cases much easier than using scanf because you won't end up in a situation where some character is stuck in the input stream.

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
  • But how to recover from that situation? Once the `'.'` is in the input stream you're somewhat "stuck". – Jabberwocky Jan 22 '19 at 13:35
  • @Jabberwocky well, you could make a loop where you removed one character from the stream and the retried the the scan. Or simply print an error message and exit. – Support Ukraine Jan 22 '19 at 13:37
0

This very simplistic program shows how you could approach the problem:

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

// Read an integer value from the user
// returns false if input stream cannot be read (usually EOF)
// Any non digit characters are discarded:
// Examples:
// "123abc" => 123
// "123.456" => 123
// "" => 0
// "abc" => 0

bool ReadIntFromUser(int *value)
{
  char inputbuffer[100];

  if (fgets(inputbuffer, sizeof inputbuffer, stdin))
  {
    *value = (int)strtoul(inputbuffer, NULL, 10);
    return true;
  }
  else
    return false;
}


int main(void) {
  int x = - 1;

  while (ReadIntFromUser(&x))
  {
    printf("Input was %d\n", x);
  }
}

Improving the ReadIntFromUser is left as an exercise to the reader.

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115