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 関数で、メモリ f が struct database の作成のためにすでに割り当てられているときに、彼がフォーム struct Address の割り当てられたメモリを呼び出すのは奇妙だと感じました。

そして、その考え方から、彼が malloc を 2 回呼び出したという事実を推論しました。最初は Connection の作成にヒープ メモリを割り当て、次に構造体 Connection 内の構造体型の作成 (つまり、Databse) は奇妙である必要があります。ええと..私は、おそらく Zed は、構造体の入れ子をレイヤーごとに構築する必要があることを知っていて理解していたと推論しました。

ここに質問を投稿する直前に、単純なネストされた構造体型を記述し、1 つの 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 呼び出しを配置し​​たのでしょうか。また、関数 Database_struct の for ループで構造体 Database にスペースを既に割り当てているのに、スタック ベースの構造体 Address オブジェクトを呼び出して、それらを前述の型の配列に配置したのはなぜですか。 ?

4

1 に答える 1

3

直接組み込むのではなく、へのポインターが含まれているDatabase_open()ため、複数の malloc 呼び出しがあります。それは次のように行うことができました:struct Connectionstruct Databasestruct Database

struct Connection {
    FILE *file;
    struct Database db;     // directly incorporate a `struct Database`
};

実際、それがあなたのstruct wtf2例で行ったことです。さまざまな理由で、ある方法が他の方法よりも優れている可能性があるようにすることができる設計上の決定があります。私は「Learn C the Hard Way」シリーズに詳しくないので、デザインの選択がうまく議論されたかどうかについてコメントすることはできません.

スタックベースの Address オブジェクトを使用して配列を初期化することに関しては、これは 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;
于 2013-10-13T04:20:26.837 に答える