2017-02-10 12 views
2

В настоящее время я изучаю C и немного нуждаюсь в помощи с моим кодом.Ошибка сегментации в результате использования fgets() для чтения строк из файла в массив структур

Скажем, есть файл под названием «books.txt», который содержит имена нескольких книг каждый в новой строке файла. Я пытаюсь захватить имена каждой книги для использования в остальной части моей программы.

Для этого я создал следующие ударил:

struct bookData { // This is my struct to encapsulate book information 
    char name[50]; // Name of book 
    // Other struct variable 
    // Other struct variable 
}; 

Теперь мне нужно получить имена каждой книги и поместить их в массив структуры. Ниже я рассказываю, как я это делал.

struct bookData booksList[numBooks]; // numBooks is the number of books in "books.txt" 

int i; 
for(i = 0; i < numBooks; i++) { 
    fgets(booksList[i].name, 50, books); 
    // Books is the "books.txt" file that was opened for reading 
} 

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

fgets(booksList[0].name, 50, books); 

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

Я пытаюсь понять, почему в моем коде возникает ошибка. Я был бы очень благодарен, если бы кто-нибудь мог дать мне совет о том, как исправить ошибку. Заранее спасибо за то, что нашли время, чтобы прочитать/ответить на мой вопрос!

EDIT: numBooks - это, по сути, количество строк в файлах «books.txt». Это приводит к количеству книг для этой конкретной проблемы. numBooks рассчитывался по следующему коду:

char c; 
int numBooks; 
while((c = fgetc(books)) != EOF) { 
    if(c == '\n') { 
     numBooks++; 
    } 
} 

EDIT # 2: Спасибо всем за вашу помощь !!!

+0

Что такое 'numBooks'? Как извлекается его ценность? – LPs

+0

Почему бы не использовать отладчик и выяснить, какая именно строка кода порождает ошибку? – DyZ

+0

Если список книг находится в стеке, а Numbooks - большой, вы можете бежать из стеков. Кроме того, после вашего возвращения данные в стеке исчезли, но вы вернули указатель на него. –

ответ

2

Следующий код является ошибочным, и можно было бы разумно ожидать, чтобы вызвать проблемы:

char c; 
int numBooks; 
while((c = fgetc(books)) != EOF) { 
    if(c == '\n') { 
     numBooks++; 
    } 
} 

Во-первых, numBooks является неиницализированные, а затем использовать это, скорее всего, чтобы вызвать неопределенное поведение.

Во-вторых, хотя и гораздо реже вызывают проблемы в большинстве систем, fgetc возвращает int, которые, как правило, имеет более широкий домен (может представлять больше значений, чем unsigned char). Это сделано по какой-то причине. Любые действительные значения символов, возвращенные fgetc, будут как unsigned char (т. Е. только положительные) значение. Отказ вызовет EOF (отрицательный только). Это означает, что fgetc может, как правило, возвращать одно из 257 значений, и, преобразовывая прямо в char, вы отбрасываете одно из этих значений: обработчик ошибок. Другими словами, вы не можете больше сказать, удалось ли fgetc или нет. Что произойдет, когда вы достигнете EOF? Вы конвертируете его в char, рассматриваете его как значение символа (когда это не так), а затем повторите попытку ..? Неверный ответ!

В заключение, fgetc возвращается int, поэтому сохраните возвращаемое значение в int ...


Другая проблема возникает, когда numBooks достигает INT_MAX и numBooks++; вызовет переполнение. Технически это неопределенное поведение и теоретически может привести к ошибкам сегментации ... но я никогда лично этого не видел. Тем не менее, вы, вероятно, должны использовать тип unsigned, так как не имеет смысла иметь отрицательный количество записей в файле, не так ли?

Подумайте об этом, если numBooks в struct bookData booksList[numBooks]; были отрицательными (или достаточно большим числом), вы можете начать видеть нарушения сегментации при доступе к более высоким элементам.

Подводя итог: Используйте неподписанный тип, когда ожидаете увидеть только положительные числа, и используйте динамическое распределение (например, malloc) для больших массивов.


Обратите внимание, что это не распространяется все возможности, как вы не дали MCVE так что это не возможно/практично сделать это; есть большая вероятность, что ваш segfault вызван другим кодом, который вы не предоставили. Пожалуйста, дайте мне знать, если вы обновите этот вопрос, чтобы я мог обновить этот ответ и сохранить мир вращением :)

+0

Большое спасибо за подробное объяснение! Это действительно помогло мне понять, что происходит. Я напечатал numBooks и обнаружил, что он вернул абсолютно нелепый номер (это был 4195735.) Теперь я буду исправлять эту часть своего кода. – Nichiren

+0

Странное число действительно. 'INT_MAX' или' UINT_MAX', возможно? 'INT_MIN' casted для 'unsigned int', возможно? Hmmm ... – Sebivor

+3

Вы забыли основную проблему:' numBooks' не инициализирован и имеет автоматическое хранилище. Это причина segfault. – LPs