Search code examples
cc89

A game of spot the difference


I have three arrays, all of which are basically the same to begin with. They all start with 50 entries, and they are all integer arrays. One of them, however, is immediately filled with large values, while the other 2 start with the usual 0,0,0,... What makes this array stand out from the rest? Following the suggestion of providing a minimal example, I have posted one below. It can probably be reduced further, but I left all of the print statements just to account for the case that this is a Heisenbug. Indeed, if I remove more lines, the problem may just fail to appear, and I want to make sure it appears so that it can be quashed.

  int main() {
   int assignments;
   int i; /*for loop index for both loops*/
   int days_late[50], scores[50], weights[50];
   scanf(" %d",&assignments);
   /*assigns assignment data from input to days_late, scores, weights*/
   for(i=0; i<assignments; i++){
     int index;
     printf(" Index scanned: %d",scanf(" %d",&index));
     printf(" Index is: %d",index);
     printf("\nScore just added is %d",scores[index]);
     printf("\nWeight just added is %d",weights[index]);
     printf("\nLateness just added is %d",days_late[index]);
     printf("\nIndex is %d",index);
     printf(" Variables scanned: %d",scanf(" %d%d%d",&scores[index],&weights[index],&days_late[index]));
     printf("\nScore just added is %d",scores[index]);
     printf("\nWeight just added is %d",weights[index]);
     printf("\nLateness just added is %d",days_late[index]);
   }
/*anything past this point is not neccessary, the error has already occurred*/
}

Output:

Index scanned: 1 Index is: 2
Score just added is -1793035504
Weight just added is 0
Lateness just added is 0
Index is 2 Variables scanned: 0
Score just added is -1793035504
Weight just added is 0
Lateness just added is 0 Index scanned: 0 Index is: 2
Score just added is -1793035504
Weight just added is 0
Lateness just added is 0
Index is 2 Variables scanned: 0
Score just added is -1793035504
Weight just added is 0
Lateness just added is 0

Seriously, what is the difference between scores and weight/lateness? Only one of them seems to be messed up from the very start.

Edit: I have nested scanf and printf to check how many variables were scanned succesfully, and the numbers returned were what I expected. So no new information.

This is the file from which input is read:

10 0 Y
2
2, 80, 40, 0
1, 100, 60, 0

The first 2 lines were processed correctly, and the variables involving them are not in the block of code above anyways. So the file might as well be

2, 80, 40, 0
1, 100, 60, 0

Solution

  • The problem is you logically attempt to put the "cart before the horse". Program flow is sequential. You need to read and store the values you are seeking before you attempt to output the stored values. In your code you attempt to output uninitialized (e.g. indeterminate) values before you have obtained input to fill the values (or initialized the values during declaration). As noted above, that leads to Undefined Behavior:

    C11 Standard - 6.7.9 Initialization(p10) "If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate." and C11 Standard - J.2 Undefined Behavior "The value of an object with automatic storage duration is used while it is indeterminate (6.2.4, 6.7.9, 6.8)."

    To get the "horse back before the cart", you need to think about what needs to happen in sequence in your code to ensure your variables are properly filled with input before you attempt to output the values. Further, if you take nothing else from the answer, learn that you cannot use any input function correctly unless you check the return to determine whether the input succeeded or failed.

    Looking at your code it appears you want to prompt the user for the number of assignments to enter and then loop taking input for the array elements for score, weight and days_late and then display what was entered to confirm the input.

    Complicating the issue, you attempt to have the user enter the index within the arrays at which the values will be stored (fine, but unnecessary if you are looping taking the input). Further, the index value would have to be within the range of elements in each of your arrays, e.g. 0 <= index < 50. It would be up to you to validate the index falls within the range before using it -- or you would invoke Undefined Behavior again by attempting to write and read values outside of your array bounds.

    To eliminate the entire index problem, since you are looping, just read the values for the assignment corresponding to the loop variable. (e.g. instead of scores[index] simply use scores[i] within your loop) That way to loop controls the index being prompted for and filled.

    Putting that together and validating each input (and simply exiting if an invalid entry is made), you could do something similar to:

    #include <stdio.h>
    
    int main (void) {
    
        int assignments,
            i,                      /* for loop index for both loops */
            days_late[50] = {0},    /* initialize arrays all zero */
            scores[50] = {0}, 
            weights[50] = {0};
    
        fputs ("\nEnter No. assignments: ", stdout);    /* prompt for no. assignments */
        /* VALIDATE EVERY INPUT - both the conversion and that the value is within range */
        if (scanf("%d", &assignments) != 1 || assignments < 0 || assignments > 49) {
            fputs ("error: invalid integer input or input out of range.\n", stderr);
            return 1;
        }
    
        /* loop assignments times */
        for (i = 0; i < assignments; i++) {
            /* display assignment no. prompt for score, weight, days_late */
            printf ("\nassignment[%2d]\nenter score, weight, days_late: ", i + 1);
            if (scanf ("%d%d%d",    /* read and VALIDATE each value */
                        &scores[i], &weights[i], &days_late[i]) != 3) {
                fputs ("error: invalid integer input - scores, weights, days_late.\n", 
                        stderr);
                return 1;
            }
            /* output values read */
            printf ("\nScore just added is %d\n"
                    "Weight just added is %d\n"
                    "Lateness just added is %d\n",
                    scores[i], weights[i], days_late[i]);
        }
        return 0;
    }
    

    Note, you can handle the error checking much more gracefully, so that the user is re-prompted until a valid entry is made (or EOF generated), but that is left to you after the input logic is resolved. See Answer to isalpha function in C not returning the correct value — flags all inputs as A-Z characters for examples.

    Example Use/Output

    $ ./bin/scoreswtsdays_late
    
    Enter No. assignments: 2
    
    assignment[ 1]
    enter score, weight, days_late: 88 1 0
    
    Score just added is 88
    Weight just added is 1
    Lateness just added is 0
    
    assignment[ 2]
    enter score, weight, days_late: 91 1 2
    
    Score just added is 91
    Weight just added is 1
    Lateness just added is 2
    

    That covers my understanding of what you are attempting. If I have misread something, please let me know and I am happy to help further. Likewise, if you need further explanation on anything above, just drop a comment below.


    Edit After Input File Format Posted

    While we are still unclear on the meaning of the first line, given your remaining description, reading assignments from line 2, and then looping assignments times reading index, scores, weights, days_late is fairly simple.

    Since you are reading a line-at-a-time, you will want to use a line-oriented input function such as fgets() (or POSIX getline()). Note line-oriented functions read and include the '\n' at the end of each line in the buffer they fill (though here parsing with sscanf, no special accommodation is needed)

    To handle your input file, just read each of the first two lines to get the needed information for reading the remainder of the file. DO NOT forget to validate that the assignments value read from line-2 is within the range of your array bounds.

    The from line-3 on, simply read the line and then parse the values from the line using sscanf validating the expected number of conversions take place for each line. The values are easily parsed from the line with the format string "%d, %d, %d, %d"

    Putting the pieces together, you could do:

    #include <stdio.h>
    
    #define MAXC 1024       /* if you need a constant, #define one (or more) */
    
    int main (void) {
    
        char buf[MAXC];             /* character array used as buffer for input */
        int assignments,
            i = 0,                  /* loop counter */
            days_late[50] = {0},    /* initialize arrays all zero */
            scores[50] = {0}, 
            weights[50] = {0};
    
        if (!fgets (buf, MAXC, stdin)) {    /* read/validate line 1 */
            fputs ("error: insufficient input - line 1\n", stderr);
            return 1;
        }
        /* parsing the 3 values left to you until description given */
        printf ("line 1: %s", buf);       /* simply output line 1 */
    
        if (!fgets (buf, MAXC, stdin)) {    /* read/validate line 2 */
            fputs ("error: insufficient input - line 1\n", stderr);
            return 1;
        }
        /* parse assignments from buf, validate in range */
        if (sscanf (buf, "%d", &assignments) != 1 || assignments < 0 || assignments > 49) {
            fputs ("error: invalid assignments values line - 2\n", stderr);
            return 1;
        }
    
        while (i < assignments && fgets (buf, MAXC, stdin)) {
            int index, score, weight, dayslate; /* temporary value to read into */
            /* parse values from line, VALIDATE 4 conversion took place */
            if (sscanf (buf, "%d, %d, %d, %d", &index, &score, &weight, &dayslate) != 4 ||
                        index < 0 || index > 49) {
                fputs ("error: invalid line format, lines 3+, or index out of range\n", 
                        stderr);
                return 1;
            }
            scores[index] = score;          /* assign values to array[index] */
            weights[index] = weight;
            days_late[index] = dayslate;
    
            /* output values read */
            printf ("\nassignment[%2d]:\n"
                    "  Score just added is   : %d\n"
                    "  Weight just added is  : %d\n"
                    "  Lateness just added is: %d\n",
                    index, scores[index], weights[index], days_late[index]);
            i++;    /* increment counter */
        }
        return 0;
    }
    

    Example Use/Output

    With your input file in dat/scoreswtsdays.txt, running the program while redirecting the data file as input would result in the following:

    $ ./bin/scoreswtsdays_late_file < dat/scoreswtsdays.txt
    line 1: 10 0 Y
    
    assignment[ 2]:
      Score just added is   : 80
      Weight just added is  : 40
      Lateness just added is: 0
    
    assignment[ 1]:
      Score just added is   : 100
      Weight just added is  : 60
      Lateness just added is: 0
    

    Again look things over and let me know if you have further questions.