SIGSEGV присвоение объекта динамической 2d-матрице

#c #segmentation-fault

#c #ошибка сегментации

Вопрос:

Я пытаюсь просто назначить элемент, определенный как Card из вектора mtr[i][j] , 2d-матрице, хранящейся в куче, но отладчик заявляет об ошибке сегментации.

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

Просто из любопытства я попытался изменить тип матрицы на int и char . Несмотря на это, присвоение (после правильного изменения значения на присвоение) не работает.

 int g_rows = 0, g_cols=0;

typedef struct{
    char color;
    int points;
}Segment;

typedef struct{
    Segment h;
    Segment v;
}Card;


Card** loadBoard(char* fileName, Card* cards) {
    short tileRef; short rot;
    short i=0;
    FILE* fp = fopen(fileName,"r"); if(fp == NULL) return NULL;
    if(fscanf(fp, "%hd %hdn", amp;g_rows, amp;g_cols) == 0) {fclose(fp); return NULL;}
    
    Card **mtr = (Card**)malloc(g_rows * sizeof(**mtr)); if(!mtr){printf("Error on first malloc.n");}
    for(short i = 0; i<g_rows; i  ) {
        mtr[i] = (Card*)malloc(g_cols * (sizeof (*mtr)) );
        if(!mtr[i]) printf("error on 2nd malloc.n");
    }
    i=0;
    while(!feof(fp)){
        for(short j=0;j<g_cols;j  ) {
            fscanf(fp, "%d/%dn", amp;tileRef, amp;rot);
            if (tileRef != -1)
                mtr[i][j] = (rot == 0) ? cards[tileRef] : (Card) {.h = cards[tileRef].v, .v = cards[tileRef].h};
            else
                mtr[i][j] = (Card){{.points=-1,.color=''},{.points=-1,.color=''}};
        }
        fscanf(fp,"bn"); i  ;
    }
    fclose(fp);
    return mtr;
}

 

Я пытался искать подобные проблемы, но слишком странно, что в итоге я изучил примеры кода для динамического распределения 2d.

Возможно, я пропустил что-то важное, но я действительно не могу это уловить.

ОКОНЧАТЕЛЬНОЕ РЕДАКТИРОВАНИЕ: я обнаружил, что виновником был компилятор, который я использовал (Clang / LLVM от MinGW). Я попытался установить Visual Studio Community в качестве набора инструментов по умолчанию в CLion, и никаких проблем не возникло.

Комментарии:

1. Есть две переменные g_rows и g_cols которые не были объявлены.

2. Почему плохо использовать feof() для управления циклом

3. @csawy Я всегда думал, что это не так безопасно, как счетчик. Я попробовал это впервые, увидев это на слайдах колледжа. Спасибо.

4. @anfauglit извините, исправлено для уточнения.

5. У вас есть некоторые ошибки в ваших fscanf вызовах. %d является спецификатором формата для int , и %hd для short int . Кажется, у вас они задом наперед. Ваш компилятор должен предупредить вас об этом, если у вас включены предупреждения (что и должно быть).

Ответ №1:

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

  1. При распределении матрицы,
     Card **mtr = (Card**)malloc(g_rows * sizeof(**mtr));
     

    malloc выделяет пространство для g_rows * sizeof(Card) . Но поскольку mtr используется для хранения указателей на карты, а не карт, мы хотим g_rows * sizeof(Card*) .

  2. Затем при выделении каждой строки матрицы,
     mtr[i] = (Card*)malloc(g_cols * (sizeof (*mtr)) );
     

    malloc выделяет пространство для g_cols * sizeof(Card*) . Но поскольку каждая строка матрицы используется для хранения карточек, а не указателей на карточки, мы хотим g_cols * sizeof(Card) . Эта ошибка, в частности, является плохой, потому что sizeof(Card *) меньше, чем sizeof(Card), поэтому выделяется недостаточно памяти.

Существует также потенциальная проблема ниже в цикле while, который считывает данные из файла. Цикл повторяется до тех пор, пока файл продолжается, без проверки, находится ли i переменная все еще в границах. Таким образом, если файл неправильно отформатирован, это может привести к тому, что этот код будет списывать конец матрицы и segfault.

Комментарий: Я настоятельно рекомендую использовать Valgrind или средство очистки адресов (ASan). Эти инструменты автоматически проверяют наличие ошибок, связанных с памятью, и имеют неоценимое значение для отладки подобных проблем.

Комментарии:

1. Существует распространенная идиома написания ptr = malloc(length * sizeof(*ptr)) . Этот код, похоже, пытается это сделать, но немного ошибается; если все сделано последовательно, первым экземпляром будет Card **mtr = malloc(g_rows * sizeof(*mtr)); и второй mtr[i] = malloc(g_cols * sizeof(*mtr[i])); .

2. @PascalGetreuer Вы правы в последней части, я уже изменил управление циклом. В первом случае я инициализировал 2d-матрицу, используя соответственно sizeof(Card*) и sizeof(Card) , но позже я неправильно изменил их, используя имя переменной. Однако исправленный код @NateEldredge по-прежнему не работает. Я попробую с ASan позже.