在Zed的流行(意见(系列中,在"堆和堆栈"一章中,数据库的代码如下
请注意,这是Zed Shaw的代码
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;
};
struct Connection *Database_open(const char *filename, char mode)
{
struct Connection *conn = malloc(sizeof(struct Connection));
if(!conn) die("Memory error");
conn->db = malloc(sizeof(struct Database));
if(!conn->db) die("Memory error");
if(mode == 'c') {
conn->file = fopen(filename, "w");
} else {
conn->file = fopen(filename, "r+");
if(conn->file) {
Database_load(conn);
}
}
if(!conn->file) die("Failed to open the file");
return conn;
}
void Database_create(struct Connection *conn)
{
int i = 0;
for(i = 0; i < MAX_ROWS; i++) {
// make a prototype to initialize it
struct Address addr = {.id = i, .set = 0};
// then just assign it
conn->db->rows[i] = addr;
}
}
我觉得在他的Database_create函数中,他调用了形式结构地址的分配内存,当内存f已经被分配用于创建结构数据库,很奇怪。
然后我通过这种思路推断出他两次调用malloc的事实,首先是为创建连接分配堆内存,然后再次用于在结构连接(即Databse(中创建结构类型也应该很奇怪。我推断,可能Zed知道并理解结构巢必须逐层构建。
就在发布问题之前,我决定编写一个简单的嵌套结构类型,并尝试通过通过一个 malloc 调用创建的最外层结构访问结构层内的数据......我的理由是,如果我没有必要逐层构建嵌套结构的想法是正确的,那么我可以在没有分割错误的情况下访问嵌套数据。我做到了
代码如下
#include<stdlib.h>
#include<stdio.h>
struct Killfly{
char str[20];
};
struct wtf
{
struct Killfly a;
};
struct wtf2{
struct wtf k;
};
int main(){
struct wtf2*fly=malloc(sizeof(struct wtf2));
printf("size of %ld n",sizeof(*fly));
fly->k.a.str[0]='a';
//printf("size of wtf is %ld n",sizeof(wtf));
free(fly);
return 0;
}
并且它没有 seg 错误地编写
问题
那么为什么 Zed 发出多个 malloc 调用,为什么当他已经为结构数据库分配空间时,在函数 Database_struct 中,在 for 循环中,他继续调用基于堆栈的结构地址对象并将它们放在所述类型的数组中?
Database_open()
中有多个 malloc 调用,因为struct Connection
包含指向struct Database
的指针,而不是直接合并struct Database
。 这本来可以这样完成:
struct Connection {
FILE *file;
struct Database db; // directly incorporate a `struct Database`
};
实际上,这就是您在struct wtf2
示例中所做的。有一些设计决策可以做到这一点,因此由于各种原因,一种方法可能比另一种方法更好。我不熟悉"Learn C the Hard Way"系列,所以我无法评论设计选择是否得到了很好的讨论。
至于使用基于堆栈的地址对象来初始化数组,这是 C 对象初始化的便利,在注释中提到制作原型:
// make a prototype to initialize it
struct Address addr = {.id = i, .set = 0};
在 C 中,没有可以直接分配给 conn->db->rows[i]
的结构文字这样的东西。但是在结构对象的声明中,可以提供初始值设定项来初始化对象。 所以这就是这里发生的事情 - struct Address
对象的声明仅用于提供能够指定初始值设定项的便利,然后该对象立即分配给堆分配结构(这是我们真正想要初始化的(。 代码序列或多或少等同于:
memset( &conn->db->rows[i], 0, sizeof(conn->db->rows[i]));
conn->db->rows[i].id = i;