Может ли структура указателей на элементы переполняться? - PullRequest
0 голосов
/ 03 мая 2019

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

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

db->entry[db_idx] = malloc(sizeof(struct scale_t)); // potiential overflow here??
db->entry[db_idx]->scale = circularlist_create();

Но я не понимаю, как это может быть, поскольку db->entry[db_idx] имеет тип struct scale_t*, а malloc возвращает указатель соответствующего типа.Сумма malloc не должна иметь большого значения, поскольку я записываю значение указателя в db->entry[db_idx]

В любом случае, здесь есть ссылка на заголовочный файл #include "CircularLinkedList.h" и файлы реализации.https://gist.github.com/jstaursky/58d4466eb232e90580e1011bf5a7e641 https://gist.github.com/jstaursky/84cf9ba2f870da0807faa454f20c36e9

файл scale.list https://gist.github.com/jstaursky/24baeaf2a922a081f0a919d31ed638df

Структура каталогов выглядит следующим образом

src
 - main.c
 - CircularLinkedList.h
 - CircularLinkedList.c
 - conf/
    - scale.list

Добавлены списки, чтобы попробовать исделайте вопрос максимально компактным.

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

#include "CircularLinkedList.h" // See linked GitHub gists.

struct scale_t {
    char* name;
    struct node_t* scale;
    int num_notes;
};

struct database_t {
    struct scale_t** entry;
    int size;
};

struct database_t *build_database(FILE *);
char *fgetline(FILE *stream);

int main(int argc, char *argv[]) 
{
    FILE *configfp = fopen("conf/scale.list", "r");
    struct database_t *scaledatabase = build_database(configfp);

    for (int i = 0; i < scaledatabase->size; ++i) {
        circularlist_traverse(scaledatabase->entry[i]->scale, circularlist_print);
    }
}

struct database_t *build_database(FILE *fp)
{
    struct database_t *db = malloc(sizeof(struct database_t));
    db->entry = malloc(sizeof(struct scale_t *));

    int db_idx = 0;

    for (char *line; (line = fgetline(fp)); ++db_idx) {
        db->entry[db_idx] = malloc(sizeof(struct scale_t)); // potiential overflow here??
        db->entry[db_idx]->scale = circularlist_create();

        char *rest = line;
        db->entry[db_idx]->name = strtok_r(line, ",", &rest);

        while (isspace(*rest))
            ++rest;

        char *interval;
        int note_count = 0;
        while ((interval = strtok_r(NULL, "-", &rest))) {
            circularlist_insert(&db->entry[db_idx]->scale, interval);
            ++note_count;
        }
        db->entry[db_idx]->num_notes = note_count;
    }
    db->size = db_idx;

    return db;
}

char*
fgetLine(FILE *stream)
{
    const size_t chunk = 128;
    size_t max = chunk;
    /* Preliminary check */

    if (!stream || feof(stream))
        return NULL;

    char *buffer = (char *)malloc(chunk * sizeof(char));

    if (!buffer) {
        perror("Unable to allocate space");
        return NULL;
    }
    char *ptr = buffer;

    int c; /* fgetc returns int. Comparing EOF w/ char may cause issues. */
    while ( (c = fgetc(stream)) != EOF && (*ptr = c) != '\n')
    {
        ++ptr;
        size_t offset = ptr - buffer;

        if (offset >= max) {
            max += chunk;
            char *tmp = realloc(buffer, max);

            if (!tmp) {
                free(buffer);
                return NULL;
            }
            buffer = tmp;
            ptr = tmp + offset;
        }
    }
    *ptr = '\0';
    return buffer;
}

1 Ответ

3 голосов
/ 03 мая 2019

Проблема в вашем коде не в структуре записи, а в структуре базы данных.

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

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

Когда вы выделяете память для базы данных:

struct database_t * db = malloc (sizeof (struct database_t));

будет выделять размер sturct, который будет размером указателя (записи) плюс размер int (размера), что означает, что указатель записи все еще является только указателем, а не массивом.

Чтобы решить эту проблему, есть несколько вещей, которые вы можете сделать:

Сохранить массив с максимальной длиной

Вы можете изменить определение структуры на что-то вроде этого:

struct database_t {
    struct scale_t* entry[MAX_LENGTH];
    int size;
};

Это сделает вашу первую операцию malloc работающей и выделит всю необходимую вам память.

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

Перераспределить память для каждой новой записи

Другим решением проблемы является выделение памяти для массива самостоятельно.

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

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

Новый код функции init должен выглядеть примерно так:

struct database_t *build_database(FILE *fp)
{
    struct database_t *db = malloc(sizeof(struct database_t));

    int db_idx = 0;
    /* Ensure that the value starts from NULL. */
    db->entry = NULL;

    for (char *line; (line = fgetline(fp)); ++db_idx) {
        /* Realloc the memory, adding the new needed memory for the new entry. */
        db->entry = realloc(db->entry, sizeof((struct scale_t *) * (db_idx + 1)));
        db->entry[db_idx] = malloc(sizeof(struct scale_t));
        db->entry[db_idx]->scale = circularlist_create();

        char *rest = line;
        db->entry[db_idx]->name = strtok_r(line, ",", &rest);

        while (isspace(*rest))
            ++rest;

        char *interval;
        int note_count = 0;
        while ((interval = strtok_r(NULL, "-", &rest))) {
            circularlist_insert(&db->entry[db_idx]->scale, interval);
            ++note_count;
        }
        db->entry[db_idx]->num_notes = note_count;
    }
    db->size = db_idx;

    return db;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...