Почему я получаю сообщение об ошибке шины с неиспользуемым указателем FILE?

Я пытаюсь написать функцию для чтения двух файлов с помощью fgets и вывода их на стандартный вывод:

void merge_files(){
    FILE *in1, *in2, *out;
    char *inName1 = "Text_4_input1.txt";
    char *inName2 = "Text_4_input2.txt";
    char *buffer;
    int n = 100;
    if ( (in1 = fopen (inName1, "r")) == NULL ) {
       printf ("Can't open %s for reading.\n", inName1);
    }
    
    if ( (in2 = fopen (inName2, "r")) == NULL ) {
       printf ("Can't open %s for reading.\n", inName2);
    }
    while (fgets (buffer, n, in1 ) !=  NULL)
    {
        fputs (buffer, stdout);
        if ( fgets (buffer, n, in2) != NULL )
        {
            printf ("a");
            fputs (buffer, stdout);
        }
    }
    while (fgets (buffer, n , in2 ) !=  NULL )
    
        fputs (buffer, stdout);
        
    fclose (in1);
    fclose (in2);
}

Я всегда получаю bus error при запуске и понял, что мой FILE *out никогда не используется. Если я удалю его из своего кода, он будет работать как положено. Почему?

char *buffer никогда ничему не присваивается — имеет неопределенное значение.
pmacfarlane 23.07.2024 20:28
char *buffer; не выделена память. В любом случае цикл кажется странным, совершая несколько вызовов fgets .
Weather Vane 23.07.2024 20:28

Кроме того: когда вы проверяете in1 и in2, вы сообщаете о проблеме, но все равно продолжаете :(

Weather Vane 23.07.2024 20:31

Насколько странным является неопределенное поведение? ФАЙЛ *out никогда не используется. Если я удалю его из своего кода, он будет работать как положено. Неназначенное значение char *buffer; оказалось в доступной памяти. Пожалуйста, включите предупреждения компилятора, чтобы получить информацию о неинициализированных переменных.

Weather Vane 23.07.2024 20:35

Как обычно: ваш компилятор предупредил бы об этом. warning: 'buffer' may be used uninitialized

AKX 23.07.2024 21:11

Спасибо за все комментарии. Я скомпилировал код с помощью gcc на MacO, но на самом деле он вообще не выдает никаких предупреждений. Вот почему я так сбит с толку. Я просто почистил код и удалил ФАЙЛ *out и всё работает. Ребята, вы просветите меня сейчас.

Anh Duc Nguyen 23.07.2024 23:18
printf ("Can't open %s for reading.\n", inName2); немного лучше обычного сообщения об ошибке, но этого все равно недостаточно. Почему системе не удалось открыть файл? Предоставьте эту информацию и запишите ее в соответствующий поток. (Все сообщения об ошибках относятся к stderr.) Вы можете добиться и того, и другого, заменив printf на perror(inName2);perror стандартизирует сообщение об ошибке, может предоставить вам бесплатную локализацию, сообщает о системной ошибке и, как правило, является хорошей идеей.
William Pursell 24.07.2024 15:45
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
7
92
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы объявляете указатель буфера:

char *buffer;

но ты ни на что не указываешь. Для небольшого буфера постоянного фиксированного размера вы можете изменить это на:

#define BUFF_SIZE 100
...
char buffer[BUFF_SIZE];

Избавьтесь от переменной n. Прочитайте данные с помощью:

while (fgets (buffer, BUFF_SIZE, in1 ) !=  NULL)

В качестве альтернативы, если у вас очень большие буферы и вы не хотите использовать для этого стек, вам следует выделить буфер из кучи. Сохраните переменную n. Затем:

int n = 100;
char *buffer = malloc(n);
if (buffer == NULL) {
  // print error and probably exit
}

Затем в конце функции:

free(buffer);

Это решит вашу непосредственную проблему. Но не забывайте, что также говорится в некоторых комментариях, например, Weather Vane, указывающий, что вы обнаруживаете ошибки fopen, но продолжаете выполнение. Вам следует выйти из программы или хотя бы вернуть звонящему плохой статус.

Спасибо, ошибка новичка, научитесь чему-нибудь заново.

Anh Duc Nguyen 23.07.2024 23:12

Переменная buffer — это дикий указатель, который ничем не инициализируется. Использование его в качестве массива приводит к неопределенному поведению (UB), что в вашем случае является «ошибкой шины». Если вы используете достаточно новый компилятор (C99), вы можете создать buffer массив переменной длины (VLA), используя переменную n. Альтернативно, используйте символическую константу, как показывает вам @StreveFord. В этом случае вы также можете жестко запрограммировать размер char buffer[100]; и использовать sizeof buffer вместо n.

Пока вы обнаружите ошибку fopen(), вы все равно продолжите.

Переменная out не используется.

Предпочитайте минимизировать область видимости переменных (объявляйте их непосредственно перед использованием). Это ясно дает понять, что у вас есть два раздела кода, которые очень похожи для открытия файла и могут нуждаться в рефакторинге в функцию. Аналогично коду, который читает и печатает файл. По крайней мере, так будет, если вы действительно хотите объединить файлы, а не чередовать не более 100-1 = 99 байтов из каждого файла с помощью "a" в качестве разделителя.

Предпочитайте одно объявление переменной в каждой строке, это поможет, как только вы начнете просматривать различия.

Предпочитайте инициализированные переменные. Это упрощает ваши два оператора if.

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

void merge_files() {
    char *inName1 = "Text_4_input1.txt";
    FILE *in1 = fopen (inName1, "r");
    if ( !in1 ) {
        printf ("Can't open %s for reading.\n", inName1);
        exit(1);
    }

    char *inName2 = "Text_4_input2.txt";
    FILE *in2 = fopen (inName2, "r");
    if ( !in2 ) {
        printf ("Can't open %s for reading.\n", inName2);
        fclose(in1);
        exit(1);
    }

    int n = 100;
    char buffer[n];
    while (fgets (buffer, n, in1 ) !=  NULL)
    {
        fputs (buffer, stdout);
        if ( fgets (buffer, n, in2) != NULL )
        {
            printf ("a");
            fputs (buffer, stdout);
        }
    }
    while (fgets (buffer, n , in2 ) !=  NULL )

        fputs (buffer, stdout);

    fclose (in1);
    fclose (in2);
}

int main() {
    merge_files();
}

и пример запуска с двумя файлами, содержащими соответственно 111\n и 222\n:

111
a222

Вы не должны возвращать значение для функции, объявленной как возвращающая void.

Streve Ford 23.07.2024 20:42

Эй, быстрый вопрос. Я не использовал VLA (наш продукт застрял на древнем C); Я думаю, они хранятся в стеке, верно?

Streve Ford 23.07.2024 20:55

@StreveFord Да, VLA (C99; необязательно C11) хранятся в стеке, и если у вас закончится место в стеке, ваша программа взорвется, и вы ничего не сможете с этим поделать. Размер стека в Linux по умолчанию составляет 8 МБ.

Allan Wind 23.07.2024 20:58

Другие вопросы по теме