结构成员指针会溢出吗?

时间:2019-05-03 19:37:32

标签: c

我正在通过双指针结构实现音阶数据库,但是在为数据库中的项目分配空间时,我一直遇到段错误和溢出错误,我不明白为什么。

这是一个概念性问题,该错误与我对指针如何工作的理解不兼容,并且我无法在线找到确定的答案。问题似乎出在这行

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 个答案:

答案 0 :(得分:3)

代码中的问题不在于条目的结构,而在于数据库的结构。

从您的代码中,您希望数据库具有一个条目数组,但是它的定义和使用并没有实现,因为它现在已经实现。

您希望数据库具有可变长度的数组作为其第一个参数(据我从您的代码中了解),但是它的定义和使用不正确。

为数据库分配内存时:

  

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

将分配结构体的大小,即指针的大小(条目)加上int的大小(size),这意味着入口指针仍然只是一个指针,而不是数组。

要解决此问题,您可以做几件事:

以最大长度保存数组

您可以将结构的定义更改为以下形式:

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;
}