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;
}
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;
}