Looping with Testing Variables

Often in my code I use a variable to both fetch data and to test the data. Before using this variable, it must be initialized, lest the test act on uninitialized garbage. A clever solution is necessary to avoid this situation.

As an example, consider int variable c in the following code, a crude file dumping program:

2020_09_19-Lesson.c

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

/* a simple file dumper */
int main(int argc, char *argv[])
{
    FILE *dumpme;
    int c;

    /* check for filename */
    if( argc < 2 )
    {
        puts("Format: dumper filename");
        exit(1);
    }

    /* open and process file */
    dumpme = fopen(argv[1],"r");
    if( dumpme==NULL )
    {
        printf("Unable to read %s\n",argv[1]);
        exit(2);
    }

    while((c=fgetc(dumpme)) != EOF)
        printf("%02X ",c);
    putchar('\n');

    fclose(dumpme);

    return(0);
}

Line 25 is a common way to express both obtaining and comparing data:

while((c=fgetc(dumpme)) != EOF)

Working inside out, the fgetc() function fetches a character from the file represented by the dumpme handle. This character is stored in int variable c. This expression is enclosed in parentheses, which yields the value stored in variable c. The value is then compared with the EOF constant. If unequal, the loop runs. When c is equal to EOF, the loop stops.

Ideally, the while loop comparison should be while(c != EOF). This comparison is less cryptic and less prone to errors or exploits. The problem is where to put the c = fgetc(dumpme) statement that fetches a character from the open file.

You could read the character before the loop, which looks like this:

c = fgetc(dumpme);
while(c != EOF)
{
    printf("%02X ",c);
    c = fgetc(dumpme);
}

This construction is legal, and it works, but it results in the fgetc() function called outside and inside the loop, which I think is kind of dumb. Still, it’s a valid and clean approach, I would offer that it’s the best way to initialize and use the variable.

Another solution, on that I use frequently, is to initialize the variable to some harmless value before the loop starts:

c = 1;
while(c != EOF)
{
    c = fgetc(dumpme);
    printf("%02X ",c);
}

A better way to initialize the variable is to set it to the opposite value being tested:

c = !EOF;
while(c != EOF)
{
    c = fgetc(dumpme);
    printf("%02X ",c);
}

Above, the value of variable c is initialized to the opposite value of EOF. The result is that the while loop test passes (naturally) and the loop proceeds.

The problem with both of these approaches is that the EOF is read and stored before the loop stops, so its value appears in the output. (The value is typically -1, which outputs as FFFFFFFF on some systems.) Because the the EOF is output, these approaches are improper.

Yet another solution, one I prefer, involves an endless loop. Use an if comparison in the loop to break out when the terminating condition is encountered:

while(1)
{
    c = fgetc(dumpme);
    if( c == EOF )
        break;
    printf("%02X ",c);
}

This solution works, and more code could be added to check for file read errors other than the EOF.

Of all these methods, I admire the c = !EOF initialize approach the best. It may not be appropriate in this Lesson’s example, but it provides a solid way to initialize a variable before it’s used, specifically to avoid a condition like c != EOF.

Leave a Reply