1

在 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 函数中,他调用了 struct Address 形式的分配内存,而内存 f 已经分配给 struct database 的创建,很奇怪。

然后我通过这种思路推断出他两次调用 malloc 的事实,首先为创建 Connection 分配堆内存,然后再为在 struct Connection(即 Databse)中创建 struct 类型应该很奇怪,因为好吧..我推断可能 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;

}

而且它写的没有段错误

问题

那么为什么 Zed 放置多个 malloc 调用,以及为什么当他已经为 struct Database 分配空间时,在函数 Database_struct 中,在 for 循环中,他继续调用基于堆栈的 struct Address 对象并将它们放置在所述类型的数组中?

4

1 回答 1

3

有多个 malloc 调用,Database_open()因为它struct Connection包含指向 a 的指针,struct Database而不是直接合并 a 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 object的声明中,可以提供一个初始化器来初始化该对象。所以这就是这里发生的事情 - 对象的声明struct Address仅用于提供能够指定初始化程序的便利,然后该对象立即分配给堆分配的结构(这是我们真正想要初始化的)。代码序列或多或少等同于:

    memset( &conn->db->rows[i], 0, sizeof(conn->db->rows[i]));
    conn->db->rows[i].id = i;
于 2013-10-13T04:20:26.837 回答