Странное поведение C с fopen и тестирование на NULL - PullRequest
0 голосов
/ 29 марта 2012

У меня есть следующий код для открытия файла ввода и вывода:

if ((source_file_ptr = fopen(source_filename, "rb")) == NULL) {
  error("unable to open input file");
}
if ((output_file_ptr = fopen(output_filename, "wb")) == NULL) {
  error("unable to open output file");
}

Это простой способ отловить ошибки при открытии файлов.

Однако после некоторого общего редактированиямоя программа, программа теперь падает, вместо того, чтобы перехватить неверный выходной файл.

Я попробовал несколько вещей без успеха, но интересно, когда я попробую это:

if ((output_file_ptr = fopen(output_filename, "wb")) == NULL) printf("fail");
exit(0);
if ((source_file_ptr = fopen(source_filename, "rb")) == NULL) {
  error("unable to open input file");
}
if ((output_file_ptr = fopen(output_filename, "wb")) == NULL) {
  error("unable to open output file");
}

Будет напечатано «сбой»(и, очевидно, выйти без особого понимания).

Но если я закомментирую строку выхода (0), он снова будет демонстрировать то же поведение при сбое, не печатая «fail» и не перехватывая ошибку.

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

Я все еще изучаю C, есть мысли о том, что здесь происходит?
Спасибо какмного!

Примечание. Заключение printf («fail») в фигурные скобки не меняет поведение, которое я наблюдаю.

РЕДАКТИРОВАТЬ: дополнительный код, который следует за выше:

if (fread(&file_struct, sizeof(file_struct), 1, source_file_ptr) < 1) {
   error("unable to read %s", source_filename);
}
else {
   error_check(file_struct);

  if (fwrite(&file_struct, sizeof(file_struct), 1, output_file_ptr) < 1) {
     error("unable to write file header");
  }
}

Ответы [ 2 ]

2 голосов
/ 29 марта 2012

«Ошибка» не обязательно будет отображаться, если у вас есть буферизованный вывод - так как он вылетает, и не завершается и, следовательно, сбрасывает выходные буферы.* setbuf (стандартный вывод, NULL);

Полагаю, вы напечатаете fail, также без exit.

Как упоминалось @caf ниже, также проверьте, является ли fflush()лучше подходит:

stdio.h

Edit: Это может быть немного беспорядочно, но, поскольку вы подтвердили, что linux + gcc я добавлю, очень короткое введение в gdb и лучший способ атаковать:

  1. Добавьте -ggdb к вашей строке компиляции 3.9 Параметры отладки вашей программы или GCC .

  2. Запустить сеанс GDB:

    • Если вы используете аргументы: $ gdb -args my_prog arg1 arg2
    • Остальное: $ gdb my_prog
    • Аргументы также могут быть предоставлены внутри GDB
  3. Установить точку останова:
    • (gdb) break 112, <- где 112 - номер белья, или </li>
    • (gdb) break main, <- где main является функцией </li>
    • (gdb) break foo.c:bar, <- где <code>foo.c - файл, а bar - функция
    • И еще несколько других способов
  4. Начало выполнения: (gdb) run

  5. Когда программа сталкивается с точкой останова, она останавливается, отсюда вы можете:

    • (gdb) print output_filename, <- вывести значение имени файла </li>
    • (gdb) print *source_file_ptr, <- вывести то, на что это указывает </li>
    • (gdb) list, list -, список белья и т. Д.<- источник печати </li>
    • и огромный список других вещей
  6. Для продолжения вы можете сделать, например:
    • (gdb) next, <- пошаговое выполнение вызовов подпрограммы. </li>
    • (gdb) step, <- пошаговоедо другой строки источника (т. е. в функцию) </li>
    • (gdb) continue, <- продолжить выполнение, до следующей точки останова, конец программы или др. </li>
    • и т. д.
  7. Для выхода: (gdb) quit

Наиболее часто используемые инструкции GDB имеют однобуквенное сокращение.Т.е. r для run, b для break, n для next и т. Д. Он также имеет завершение табуляции для известных переменных.

Многие команды также принимают числовые значения,или другой, аргумент.Т.е. next 8

Используйте руководство, справочные страницы и т. Д., А во время работы вы также можете вводить справочные команды.Т.е. (gdb) help run

2 голосов
/ 29 марта 2012

Первое, что я проверю, это то, что source_filename была допустимой строкой Си. Затем я бы проверил output_filename таким же образом.

Если выяснится, что ваши изменения каким-то образом повредили одно или оба, вы находитесь в неопределенном поведении территории.

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

Другая вещь, которую вы должны проверить, это то, что ваши звонки на error действительно возвращаются (а не, например, на звонок exit).

Если они это сделают, то, хотя вызовы fopen вернули NULL, вы все равно собираетесь передать это fread и / или fwrite, определенное нет-нет.

Так как ваш второй случай печатает "fail", это определенно, что у вас есть некоторые проблемы с открытием выходного файла. Использование дескриптора NULL из этого не было бы хорошо.

Если эта функция возвращает , то вы должны использовать что-то вроде:

if ((source_file_ptr = fopen(source_filename, "rb")) == NULL) {
  error("unable to open input file");
  return;
}
if ((output_file_ptr = fopen(output_filename, "wb")) == NULL) {
  fclose (source_file_ptr);
  error("unable to open output file");
  return;
}
...