3

我正在尝试使用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));

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

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)意味着它正在尝试读取它无法读取的内容

4

3 回答 3

2

sizeof(struct Address)不想sizeof(struct Address*)

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

于 2012-06-22T13:04:50.413 回答
1

编辑:好吧,看起来(使用您的最新编辑),您实际上正在正确地完成这部分。

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

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));
于 2012-06-22T13:19:30.167 回答
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 Addresses),而只是指向内存位置的指针。在读取以前编写的数据库时,该指针不指向任何特定的东西,它是一个野指针。然后当然尝试取消引用它很可能会导致分段错误,如果没有,您将得到无意义的伪数据。

如果您更改您的struct Databaseso that rowsis a struct Address*,您需要保留项目计数并更改您的读写代码以处理指向的数据rows。先写下你有多少个项目,然后写剩下的(max_datamax_rows项目);阅读时,阅读你有多少项目,为它们分配空间,阅读max_datamax_rows项目。

于 2012-06-22T16:16:17.703 回答