我正在尝试使用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)
,我认为这意味着它试图读取它不能的东西
答案 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
以使rows
为struct Address*
,则需要保留项目数并更改您的读写代码以处理指向的数据rows
。首先写下你有多少项,然后写下其余的项目(max_data
,max_rows
和项目);阅读时,请阅读您拥有的项目数,为其分配空间,阅读max_data
和max_rows
以及项目。