0

I found some challenge on reddit to make a program which will sum up all DnD dice rolls. Number of throws is unlimited therefore I created this while loop.

I used fgets to input the string, (I can't input only integers because the input is for example 1d3, where 1 is number of dice thrown, and 3 is number of sides of the dice thrown.)

When the user is prompted to input dice, fgets never stops reading user input.

For example:

To end inputting dice type 0
1d3 
1d4
1d5
0
0
^C

Main function:

int main(void)
{
    char input[MAXSIZE];
    int sum = 0;
    printf("To end inputting dice type 0\n");
    while(*(input) != 0);
    {
        fgets(input, sizeof(input), stdin);
        printf("Debug: input = ");
        puts(input);
        printf("\n");
        sum += dice(input);
        printf("Debug: sum = %d\n", sum);
    }
    printf("Sum of dice rolls is %d.", sum);
    return 0;
}
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • You have found the culprit (also notice Sourav's correct mention of `0` not being the same as `'0'` in your `while` condition); but there is more that can be improved. You should check that `input[ strlen( input ) -1 ] == '\n'`. If it isn't, the line you read was incomplete. Also: 1) `#include ` missing. 2) `MAXSIZE` undeclared. 3) function `dice` undeclared. Please use [mcve]s. – DevSolar Jul 16 '18 at 12:49
  • 1
    @DevSolar `input[ strlen( input ) -1 ] == '\n'` sets up a hacker exploit when the first character read is a _null character_. – chux - Reinstate Monica Jul 16 '18 at 13:28
  • @chux: Sheesh... good point. I'll let my comment stand though, as the general gist still holds. (Good thing I never wrote C in places where this would have mattered...) – DevSolar Jul 16 '18 at 13:31

4 Answers4

3

Firstly, the literal value of the character input 0 is not 0. In ASCII, it is 48 (decimal).

Try

 while(*(input) != '0')  // (1) - use the character literal form
                         // (2) remove the ;

That said, the standard output is usually line buffered. You need to force a flush if you want to see the outputs in the terminal. You can do that by either

  • add a newline

     printf("Debug: input = \n");
    
  • use fflush(stdout).
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
1

Try this:-

while(fgets(input, sizeof input, stdin) != NULL)

or

while(fgets(input, sizeof input, stdin))
Mr. Roshan
  • 1,777
  • 13
  • 33
1

The issue was really simple and such a beginner mistake I feel shameful for even asking the question.

The semicolon after the while loop.

Thanks all for helping me out.

0
char input[MAXSIZE] = { 0 }; // initialise input!
// otherwise you get to here and access an uninitialised variable:
while(*(input) != 0); // <--- and a semicolon right there!!! Remove that!

In fact I think the loop you want is while (fgets(input, sizeof input, stdin) && strcmp(input, "0\n"))... note that I've hoisted the fgets into the loops control expression.

You should probably do a check after calling fgets to ensure a newline is read, for example

while (fgets(input, sizeof input, stdin) && strcmp(input, "0\n")) {
    size_t n = strcspn(input, "\n");
    if (input[n] == '\n') input[n] = '\0';
    else assert(input[n] == '\0'), // #include <assert.h>
         fscanf(stdin, "%*[^\n]"),
         fgetc(stdin);

There's no undefined behaviour associated with reading unsigned integers when using fscanf, so if you only plan on using positive values you can use that instead of fgets if you wish, i.e.

unsigned dice_count, dice_sides;
while (fscanf(stdin, "%ud%u", &dice_count, &dice_sides) == 2) {
    printf("You chose to roll %u times with dice that contain %u sides\n", dice_count, dice_sides);
}
autistic
  • 1
  • 3
  • 35
  • 80