Я пытаюсь написать функцию для чтения двух файлов с помощью 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;
не выделена память. В любом случае цикл кажется странным, совершая несколько вызовов fgets
.
Кроме того: когда вы проверяете in1
и in2
, вы сообщаете о проблеме, но все равно продолжаете :(
Насколько странным является неопределенное поведение? ФАЙЛ *out
никогда не используется. Если я удалю его из своего кода, он будет работать как положено. Неназначенное значение char *buffer;
оказалось в доступной памяти. Пожалуйста, включите предупреждения компилятора, чтобы получить информацию о неинициализированных переменных.
Как обычно: ваш компилятор предупредил бы об этом. warning: 'buffer' may be used uninitialized
Спасибо за все комментарии. Я скомпилировал код с помощью gcc на MacO, но на самом деле он вообще не выдает никаких предупреждений. Вот почему я так сбит с толку. Я просто почистил код и удалил ФАЙЛ *out
и всё работает. Ребята, вы просветите меня сейчас.
printf ("Can't open %s for reading.\n", inName2);
немного лучше обычного сообщения об ошибке, но этого все равно недостаточно. Почему системе не удалось открыть файл? Предоставьте эту информацию и запишите ее в соответствующий поток. (Все сообщения об ошибках относятся к stderr.) Вы можете добиться и того, и другого, заменив printf
на perror(inName2);
perror
стандартизирует сообщение об ошибке, может предоставить вам бесплатную локализацию, сообщает о системной ошибке и, как правило, является хорошей идеей.
Вы объявляете указатель буфера:
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, но продолжаете выполнение. Вам следует выйти из программы или хотя бы вернуть звонящему плохой статус.
Спасибо, ошибка новичка, научитесь чему-нибудь заново.
Переменная 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.
Эй, быстрый вопрос. Я не использовал VLA (наш продукт застрял на древнем C); Я думаю, они хранятся в стеке, верно?
@StreveFord Да, VLA (C99; необязательно C11) хранятся в стеке, и если у вас закончится место в стеке, ваша программа взорвется, и вы ничего не сможете с этим поделать. Размер стека в Linux по умолчанию составляет 8 МБ.
char *buffer
никогда ничему не присваивается — имеет неопределенное значение.