C中的动态大小的数组

时间:2012-06-22 13:03:09

标签: c pointers

我正在尝试使用http://c.learncodethehardway.org/来学习C,但我仍然坚持第18章中的一个额外学分问题(http://c.learncodethehardway.org/book/learn-c-the- hard-waych18.html)我希望有人可以帮助我。

我遇到的具体问题是有几个结构定义如下:

#define MAX_ROWS = 500;
#define MAX_DATA = 512;

struct Address {
    int id;
    int set;
    char name[MAX_DATA];
    char email[MAX_DATA];
};

struct Database {
    struct Address rows[MAX_ROWS];       
};     

struct Connection {
    FILE *file;
    struct Database *db;
}; 

面临的挑战是重做,以便rows可以具有不依赖于该常量的可变大小。

因此,在我的Database_create方法中,我尝试使用以下内容初始化rows

conn->db->rows = (struct Address*) malloc(max_rows * sizeof(struct Address));

其中conn->db指向数据库实例,max_rows是一个传递给函数的int。 我还将Database结构更改为

struct Database{
    struct Address* rows;
}

这段代码似乎运行正常,但如果我尝试访问rows的任何成员,我会遇到分段错误,我相信这意味着我正在尝试访问未使用的内存位。

我已经花了好几个小时在这上面,我确信我不能太远,但我真的很感激任何指导让我走上正轨。


编辑:只是想在用Valgrind运行之后再添加一些细节,这会引发错误:

==11972== Invalid read of size 4
==11972==    at 0x100001578: Database_set (ex18.c:107)
==11972==    by 0x100001A2F: main (ex18.c:175)
==11972==  Address 0x7febac00140c is not stack'd, malloc'd or (recently) free'd

它指向的代码行是:

struct Address *addr = &conn->db->rows[id];
if(addr->set) die("Already set, delete it first");   

第107行是if(addr->set),我认为这意味着它试图读取它不能的东西

3 个答案:

答案 0 :(得分:2)

您希望sizeof(struct Address)不是sizeof(struct Address*)

sizeof(struct Address*)可能返回4的大小(完全取决于目标平台),而Address结构的实际大小更接近1040(假设每个字符1个字节,每个4个字节) INT)

答案 1 :(得分:1)

编辑:嗯,看起来(使用您的最新编辑),您实际上正在正确地执行此部分。

实际上,您并没有为Address结构分配足够的大小。你需要的是这样的:

struct Database{
    struct Address** rows;
}
//create array of pointers to Address structures
// (each element has size of the pointer)
conn->db->rows = (struct Address**) malloc(max_rows * sizeof(struct Address*));
for(int i=0; i < max_rows; i++) {
    conn->db->rows[i] = (struct Address*) malloc(sizeof(struct Address));
}

或者这个:

struct Database{
    struct Address* rows;
}
//create array of Address structures
// (each element has size of the structure)
conn->db->rows = (struct Address*) malloc(max_rows * sizeof(struct Address));

答案 2 :(得分:1)

加载时

void Database_load(struct Connection *conn)
{
        int rc = fread(conn->db, sizeof(struct Database), 1, conn->file);
        if(rc != 1) die("Failed to load database");
}

或编写数据库,

void Database_write(struct Connection *conn)
{
        rewind(conn->file);

        int rc = fwrite(conn->db, sizeof(struct Database), 1, conn->file);
        if(rc != 1) die("Failed to write database");

        rc = fflush(conn->file);
        if(rc == -1) die("Cannot flush database.");
}

您不是在读取或写入内容(struct Address es),​​而只是指向内存位置的指针。当读取以前写入的数据库时,该指针不指向任何特定的数据,它是一个狂野的指针。然后当然试图取消引用它很可能会导致分段错误,如果它没有,你会得到无意义的伪数据。

如果您更改struct Database以使rowsstruct Address*,则需要保留项目数并更改您的读写代码以处理指向的数据rows。首先写下你有多少项,然后写下其余的项目(max_datamax_rows和项目);阅读时,请阅读您拥有的项目数,为其分配空间,阅读max_datamax_rows以及项目。