выходные данные fgetc меняются, когда код помещается внутри функции и выполняется

#c #file #strlen #fgetc #file-pointer

#c #файл #strlen #fgetc #указатель на файл

Вопрос:

Во-первых, позвольте мне сказать, что я прошу прощения за отсутствие конкретики в этом названии. Честно говоря, не было хорошего способа описать то, с чем я столкнулся, в одном предложении, если честно, но я постараюсь описать это как можно лучше.

Я экспериментировал с использованием функций обработки файлов C. Я тестировал способы применения функции fgetc к моему назначению структур данных, над которым я работаю для колледжа. Я без особых трудностей понял, как я собираюсь его использовать, но затем я столкнулся с чем-то странным, когда тестировал разные выходные данные кода.

По сути, я пытался извлечь имя и фамилию человека из текстового файла, который мой профессор дал как часть задания. Мне удалось это сделать, но я заметил, как изменился вывод массива строк / символов в зависимости от того, как и где он выполнялся

Ниже приведен код, который выполняется:

 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("%sn", name);
printf("Pos: %d Strlength: %dn", pos, strlen(name));
name[pos] = ''; //Basically truncating the string with this here
printf("%sn", name);
printf("Pos: %d Strlength: %dn", pos, strlen(name));
//Close File
fclose(fptr);
 

Когда я запускаю приведенный выше код только внутри основной функции, на экране выводится следующее:

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

Однако, если я помещаю тот же код внутри функции, которую я вызвал «fromFunc», и вызываю свою основную функцию fromFunc, это то, что выводится вместо этого:

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

Я не могу понять, почему это происходит. Я попытался сделать все мои переменные глобальными, что ничего не изменило. Я пробовал передавать значения по указателю и по ссылке на функцию; вывод все равно не изменился. В настоящее время я использую Codeblocks 20.03 с компилятором GNU GCC, чтобы проверить это, поэтому я не могу сказать, происходит ли это с другими IDE или компиляторами. При необходимости содержимое используемого мной текстового файла показано ниже:

 Name    Mileage Years   Sequence
Rose King   93000   2   1
 

И для потомков, вот весь исходный код:

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

void fromFunc()
{
    printf("----------------------------Beginning of fromFuncn");
    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("%sn", name);
    printf("Pos: %d Strlength: %dn", pos, strlen(name));
    name[pos] = '';
    printf("%sn", name);
    printf("Pos: %d Strlength: %dn", pos, strlen(name));
    //Close File
    fclose(fptr);
    printf("----------------------------End of fromFuncn");
}

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("%sn", name);
    printf("Pos: %d Strlength: %dn", pos, strlen(name));
    name[pos] = '';
    printf("%sn", name);
    printf("Pos: %d Strlength: %dn", pos, strlen(name));

    fclose(fptr);
    }
    printf("----------------------------End of Mainn");
    return 0;
}
 

Комментарии:

1. Вы печатаете name до того, как он завершается нулевым значением.

2. Переменные могут иметь случайные значения по умолчанию, в зависимости от того, как чувствует себя компилятор в тот день. В main вы не получили нулевой терминатор после 9 символов. В fromFunc вы сделали — потому что это случайно

Ответ №1:

Вы вызвали неопределенное поведение, используя значения неинициализированной нестатической локальной переменной name[pos] , которая является неопределенной, для первой printf("%sn", name); в каждой функции.

При вызове неопределенного поведения допускается все, что угодно.

Ответ №2:

Вы должны явно сохранить нулевой байт в конце name массива с name[pos] = ''; после циклов чтения. Без этого строка может быть неправильно завершена нулем, поскольку name это локальный неинициализированный объект, его элементы неопределенны. Иногда name[pos] случается, что у вас уже есть нулевой байт, иногда нет, как вы заметили, это называется неопределенным поведением.

Вот модифицированная версия:

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

void fromFunc() [
    printf("----------------------------Beginning of fromFuncn");
    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 amp;amp; isprint(c) != 0) { //Read characters until a non-printable character is reached
        name[pos  ] = c;
        c = fgetc(fptr);
    }
    name[pos] = '';
    printf("%sn", name);
    printf("Pos: %d Strlength: %dn", pos, strlen(name));
    //Close File
    fclose(fptr);
    printf("----------------------------End of fromFuncn");
}

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 amp;amp; 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] = '';
        printf("%sn", name);
        printf("Pos: %d Strlength: %dn", pos, strlen(name));
        fclose(fptr);
    }
    printf("----------------------------End of Mainn");
    return 0;
}