fgetc output varies when code is placed inside function and executed

75 Views Asked by At

First, let me say that I apologize for the lack of specificity in this title. There honestly was no good way to describe what I encountered in a single sentence to be honest, but I will try to describe this as best as I can.

I was experimenting with the use of C's file handling functions. I had been testing ways I could apply the fgetc function to my Data Structures assignment I'm working on for college. I figured out how I was going to use it without much difficulty, but then I came across something weird when I was testing different code outputs.

Basically, I was attempting to pull a person's first and last name from a text file my professor gave as part of the assignment. I managed to do that, but I noticed how the string/char array output changed based on how and where it was being executed

Below is the code that is being run:

FILE *fptr;
char c;
char name[20];
int pos = 0; //integer representing position of char to be written in name char array
// Open file
fptr = fopen("Anomaly.txt", "r");
if (fptr == NULL)
{
    printf("Cannot open file \n");
    exit(0);
}
// Read contents from file
//27th character is last letter of first line. After that there is a newline character.
//So technically, after 28 characters we will have the second line's characters
for (int i = 0; i < 28; i++) //ignores the first line as it does not contain customer information
{
c = fgetc(fptr);
}
c = fgetc(fptr); //char c = first character in the second line (which is 'R')
while (isprint(c) != 0) //Read characters until a non-printable character is reached
{
    name[pos++] = c;
    c = fgetc(fptr);
}
printf("%s\n", name);
printf("Pos: %d Strlength: %d\n", pos, strlen(name));
name[pos] = '\0'; //Basically truncating the string with this here
printf("%s\n", name);
printf("Pos: %d Strlength: %d\n", pos, strlen(name));
//Close File
fclose(fptr);

When I run the above code inside only the main function, what prints to the screen is this:

----------------------------Beginning of Main:
Rose King§@
Pos: 9 Strlength: 11
Rose King
Pos: 9 Strlength: 9
----------------------------End of Main

However, if I place that same code inside a function I called "fromFunc", and have my Main function call fromFunc, this is what is outputted instead:

----------------------------Beginning of fromFunc
Rose King
Pos: 9 Strlength: 9
Rose King
Pos: 9 Strlength: 9
----------------------------End of fromFunc

I can't understand why this occurs. I tried making all my variables global which didn't change anything. I tried passing values by pointer and by reference to the function; still didn't change the output. I'm currently using Codeblocks 20.03 with GNU GCC compiler to test this, so I can't say whether or not this occurs with other IDEs or compilers. If needed, the contents of the txt file I was using are shown below:

Name    Mileage Years   Sequence
Rose King   93000   2   1

And for posterity, here is the entire source code:

#include <stdio.h>
#include <stdlib.h> // For exit()
#include <string.h>

void fromFunc()
{
    printf("----------------------------Beginning of fromFunc\n");
    FILE *fptr;
    char c;
    char name[20];
    int pos = 0; //integer representing position of char to be written in name char array
    // Open file
    fptr = fopen("Anomaly.txt", "r");
    if (fptr == NULL)
    {
        printf("Cannot open file \n");
        exit(0);
    }
    // Read contents from file
    //27th character is last letter of first line. After that there is a newline character.
    //So technically, after 28 characters we will have the second line's characters
    for (int i = 0; i < 28; i++) //ignores the first line as it does not contain customer information
    {
    c = fgetc(fptr);
    }
    c = fgetc(fptr); //char c = first character in the second line (which is 'R')
    while (isprint(c) != 0) //Read characters until a non-printable character is reached
    {
        name[pos++] = c;
        c = fgetc(fptr);
    }
    printf("%s\n", name);
    printf("Pos: %d Strlength: %d\n", pos, strlen(name));
    name[pos] = '\0';
    printf("%s\n", name);
    printf("Pos: %d Strlength: %d\n", pos, strlen(name));
    //Close File
    fclose(fptr);
    printf("----------------------------End of fromFunc\n");
}

int main()
{
    fromFunc();
    printf("----------------------------Beginning of Main:\n");
    {
    FILE *fptr;
    char c;
    char name[20];
    int pos = 0; //integer representing position of char to be written in name char array
    // Open file
    fptr = fopen("Anomaly.txt", "r");
    if (fptr == NULL)
    {
        printf("Cannot open file \n");
        exit(0);
    }
    for (int i = 0; i < 28; i++) //ignore the first line as it does not contain customer information
    {
    c = fgetc(fptr);
    //printf ("%c", c);
    }
    c = fgetc(fptr); //char c = first character in the second line (which is 'R')
    while (isprint(c) != 0)
    {
        name[pos++] = c; //pos++ is in brackets here because it saves an extra line. Without it, we'd have to add ++pos at the end of the while loop
        c = fgetc(fptr);
    }
    printf("%s\n", name);
    printf("Pos: %d Strlength: %d\n", pos, strlen(name));
    name[pos] = '\0';
    printf("%s\n", name);
    printf("Pos: %d Strlength: %d\n", pos, strlen(name));

    fclose(fptr);
    }
    printf("----------------------------End of Main\n");
    return 0;
}
2

There are 2 best solutions below

0
On BEST ANSWER

You must explicitly store a null byte at the end of the name array with name[pos] = '\0'; after the reading loops. Without this, the string might not be properly null terminated as name is a local uninitialized object, its elements are indeterminate. Sometime name[pos] happens to already have a null byte, sometimes not, as you observe, this is called undefined behavior.

Here is a modified version:

#include <stdio.h>
#include <stdlib.h> // For exit()
#include <string.h>

void fromFunc() [
    printf("----------------------------Beginning of fromFunc\n");
    FILE *fptr;
    int c;
    char name[20];
    int pos = 0; //integer representing position of char to be written in name char array
    // Open file
    fptr = fopen("Anomaly.txt", "r");
    if (fptr == NULL) {
        printf("Cannot open file \n");
        exit(0);
    }
    // Read contents from file
    //27th character is last letter of first line. After that there is a newline character.
    //So technically, after 28 characters we will have the second line's characters
    for (int i = 0; i < 28; i++) { //ignores the first line as it does not contain customer information
        c = fgetc(fptr);
    }
    c = fgetc(fptr); //char c = first character in the second line (which is 'R')
    while (pos < 19 && isprint(c) != 0) { //Read characters until a non-printable character is reached
        name[pos++] = c;
        c = fgetc(fptr);
    }
    name[pos] = '\0';
    printf("%s\n", name);
    printf("Pos: %d Strlength: %d\n", pos, strlen(name));
    //Close File
    fclose(fptr);
    printf("----------------------------End of fromFunc\n");
}

int main()
{
    fromFunc();
    printf("----------------------------Beginning of Main:\n");
    {
        FILE *fptr;
        int c;
        char name[20];
        int pos = 0; //integer representing position of char to be written in name char array
        // Open file
        fptr = fopen("Anomaly.txt", "r");
        if (fptr == NULL) {
            printf("Cannot open file \n");
            exit(0);
        }
        for (int i = 0; i < 28; i++) { //ignore the first line as it does not contain customer information
            c = fgetc(fptr);
            //printf ("%c", c);
        }
        c = fgetc(fptr); //char c = first character in the second line (which is 'R')
        while (pos < 1 && isprint(c) != 0) {
            name[pos++] = c; //pos++ is in brackets here because it saves an extra line. Without it, we'd have to add ++pos at the end of the while loop
            c = fgetc(fptr);
        }
        name[pos] = '\0';
        printf("%s\n", name);
        printf("Pos: %d Strlength: %d\n", pos, strlen(name));
        fclose(fptr);
    }
    printf("----------------------------End of Main\n");
    return 0;
}
0
On

You invoked undefined behavior by using values of uninitialized non-static local variable name[pos], which is indeterminate, on the first printf("%s\n", name); in each functions.

Anything is allowed to happen when undefined behavior is invoked.