Почему этот код C выдает ошибку шины?

Я, как обычно, читал здесь немало постов. Я нашел особенно полезные сообщения об ошибках шины в целом, см. здесь. Моя проблема в том, что я не могу понять, почему мой конкретный код выдает ошибку.

Мой код - это попытка научить себя C. Это модификация игры, которую я сделал, когда изучал Java. Цель моей игры - получить огромный текстовый файл размером 5049 x 1. Случайно выберите слово, перемешайте его и попытайтесь угадать. Я знаю, как все это делать. Так или иначе, каждая строка текстового файла содержит такое слово, как:

   5049
   must
   lean 
   better 
   program 
   now
   ...

Итак, я создал массив строк в C, попытался прочитать этот массив строк и поместить его в C. Больше я ничего не делал. Как только я помещу файл в C, все остальное будет легко. Еще более странно то, что он соответствует. Моя проблема возникает, когда я запускаю его с помощью команды ./blah.

Ошибка, которую я получаю, проста. Он говорит:

zsh: bus error ./blah

Мой код ниже. Я подозреваю, что это может быть связано с памятью или переполнением буфера, но это совершенно ненаучно и интуитивно. Итак, мой вопрос прост, почему этот код C выдает мне сообщение об ошибке шины?

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

//Preprocessed Functions 
void jumblegame();
void readFile(char* [], int);


int main(int argc, char* argv[])
{
    jumblegame();

}

void jumblegame()
{
    //Load File 
        int x = 5049; //Rows
        int y = 256; //Colums
        char* words[x]; 
        readFile(words,x);

    //Define score variables 
        int totalScore = 0;
        int currentScore = 0; 

   //Repeatedly pick a random work, randomly jumble it, and let the user guess what it is

}

void readFile(char* array[5049], int x) 
{
    char line[256]; //This is to to grab each string in the file and put it in a line. 
    FILE *file;
    file = fopen("words.txt","r");

    //Check to make sure file can open 
    if(file == NULL)
    {
        printf("Error: File does not open.");
        exit(1);
    }
    //Otherwise, read file into array  
    else
    {
        while(!feof(file))//The file will loop until end of file
        {
           if((fgets(line,256,file))!= NULL)//If the line isn't empty
           {
               array[x] = fgets(line,256,file);//store string in line x of array 
               x++; //Increment to the next line 
           }    
        }
    }

}

person GeekyOmega    schedule 30.07.2012    source источник


Ответы (6)


У этой строки есть несколько проблем:

array[x] = fgets(line,256,file);//store string in line x of array 
  • Вы уже прочитали строку в условии непосредственно предшествующего оператора if: текущая строка, с которой вы хотите работать, уже находится в буфере, и теперь вы используете fgets, чтобы получить следующий строка.

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

  • Наконец, вы пытаетесь скопировать строки с помощью =. Это только скопирует ссылки, но не создаст новую копию строки. Таким образом, каждый элемент массива будет указывать на один и тот же буфер: line, который выйдет из области видимости и станет недействительным, когда ваша функция завершится. Чтобы заполнить ваш array строками, вам нужно сделать копию каждой из них для массива: выделить место для каждой новой строки с помощью malloc, затем использовать _ 8_, чтобы скопировать каждый line в новую строку. В качестве альтернативы, если вы можете использовать strdup, он позаботится о выделении место для тебя.

Но я подозреваю, что это причина вашей ошибки шины: вы передаете массив size как x, а в своем цикле вы назначаете array[x]. Проблема в том, что array[x] не принадлежит массиву, массив имеет только используемые индексы от 0 до (x - 1).

person pb2q    schedule 30.07.2012
comment
array [] все равно не существует :( - person Martin James; 30.07.2012
comment
Чтобы уточнить, указатель, который вы пишете в array[x], в настоящее время является указателем на line. line освобождается, когда readFile возвращается, что делает все эти указатели недействительными. - person Keith Randall; 30.07.2012
comment
Спасибо. Это ответило на мой вопрос. Я также использовал следующую ссылку, чтобы выяснить, как скопировать строку в массив: stackoverflow.com/questions/1088622/ - person GeekyOmega; 30.07.2012
comment
Я должен признать, что для меня это немного похоже на черную магию, поскольку я изучаю буферы, malloc и все эти вещи, которые Java и C # делают автоматически. Можете ли вы порекомендовать мне хороший источник для чтения или книгу, объясняющую это на C? В противном случае мне кажется, что я просто использую черную магию и плохо понимаю принципы Си. :-( - person GeekyOmega; 30.07.2012
comment
Вам нужно выяснить указатели, чтобы использовать C. Я рекомендую Язык программирования C < / a>, см. также. Это небольшая книга, но наполненная полезной информацией. Выполняйте все упражнения, и это уже не будет казаться таким уж эзотерическим. Эта книга устарела, но остается очень полезной, и я не видел более сжатого текста на языке C. Также см. Раздел pointers в этом руководстве - person pb2q; 30.07.2012
comment
Спасибо. Я также провел несколько поисков в Google по структурам данных в C. Но есть ли у вас какие-нибудь фавориты? - person GeekyOmega; 31.07.2012

Вы передаете значение 5049 для x. В первый раз, когда линия

array[x] = ... 

выполняется, он обращается к несуществующему местоположению массива.

Похоже, вы изучаете C. Отлично! Навык, который вам необходимо освоить на раннем этапе, - это базовое использование отладчика. В этом случае, если вы скомпилируете свою программу с

gcc -g myprogram.c -o myprogram

а затем запустите его с помощью

gdb ./myprogram

(Я предполагаю, что Linux), вы получите дамп стека, который показывает строку, в которой произошла ошибка шины. Этого должно быть достаточно, чтобы помочь вам выяснить ошибку самостоятельно, что в конечном итоге намного лучше, чем спрашивать других.

Есть много других способов использования отладчика, но он занимает первое место в списке. Это дает вам окно в вашу запущенную программу.

person Gene    schedule 30.07.2012
comment
Спасибо. Я просто погуглил gdb и запустил его. Он сообщил о проблеме с памятью в файле чтения, что было общим ключом к тому, что конкретно было указано неправильно выше, а именно к использованию мной fget (). Сейчас работаю над исправлением. (Я учусь использовать linux и unix, а также vim). - person GeekyOmega; 30.07.2012

Вы сохраняете строки в строковом буфере, который определен внутри функции readFile, и сохраняете указатели на него в arary. С этим связаны две проблемы: вы перезаписываете значение каждый раз, когда читается новая строка и буфер находится в стеке, и становится недействительным после возврата из функции.

person AlexDev    schedule 30.07.2012

char* array[5049], int x
array[x] = fgets(line,256,file)

Вы присваиваете массив [x], который является ячейкой памяти длиной = 1, вы назначаете указатель (4 байта).

С другой стороны, вы читаете 2 раза по 256 байтов и теряете первые 256.

Но большая ошибка заключается в том, что вы ставите последнее условие! Feof (), которое не проверяет, что предел массива строк дает переполнение.

person alinsoar    schedule 30.07.2012
comment
Согласованный. Это была такая глупая ошибка, и я ее исправил. Спасибо. - person GeekyOmega; 30.07.2012

У вас как минимум несколько проблем:

  • array[x] = fgets(line,256,file)

    Это сохраняет адрес line в каждом элементе массива. line in больше не действует, когда readFile() возвращается, поэтому у вас будет массив бесполезных указателей. Даже если бы line имел более длительный срок службы, было бы бесполезно иметь все элементы вашего массива с одним и тем же указателем (каждый из них просто указывал бы на то, что было записано в буфер последним)

  • while(!feof(file))

    Это антипаттерн для чтения файла. См. http://c-faq.com/stdio/feof.html и "Неправильное использование feof ()". Этот антипаттерн, вероятно, ответственен за зацикливание вашей программы в большей степени, чем вы могли ожидать при чтении файла.

  • вы выделяете массив для хранения 5049 указателей, но вы просто читаете столько, сколько находится в файле - нет проверки того, читаете ли вы ожидаемое число, или для предотвращения чтения слишком большого количества. Вам следует подумать о динамическом распределении массива при чтении файла или иметь механизм, гарантирующий, что вы читаете правильный объем данных (не слишком мало и не слишком много) и обрабатываете ошибку, когда она неправильная.

person Michael Burr    schedule 30.07.2012
comment
У меня сжалось сердце, когда я увидел, что могу неправильно использовать feof. Я, вероятно, поработаю над тем, как получить эту работу напрямую, используя fread. И я предпочитаю делать что-то динамически, но один друг сказал мне, что C часто просто помещает числа, которые работают. Звучит так, будто я получил плохой совет. - person GeekyOmega; 30.07.2012
comment
Ввод чисел, которые работают, может быть нормальным, но должна быть предусмотрена обработка ошибок, когда данные превышают эти числа (или когда данные не достигают ожидаемого числа, если это важно). - person Michael Burr; 31.07.2012

Я подозреваю, что проблема в (fgets(line,256,file))!=NULL). Лучше читать файл с помощью fread() (см. http://www.cplusplus.com/reference/clibrary/cstdio/fread/). Укажите FILE* (файловый поток в C), размер буфера и буфер. Подпрограмма возвращает количество прочитанных байтов. Если возвращаемое значение равно нулю, то достигнут конец EOF.

char buff [256]; 
fread (file, sizeof(char), 256, buff); 
person tweaksp    schedule 30.07.2012
comment
Спасибо. Я собираюсь попробовать это, как только моя программа будет работать так, как у меня есть. Я считаю, что это действительно может быть лучшим решением. - person GeekyOmega; 30.07.2012