2017-01-18 8 views
2

私はそれで要素を初期化して置くために動作するようにマップどのように構造体の動的配列にデータを格納するのですか?

typedef struct { 
    const char *name; 
    int number; 
} Entry; 

typedef struct { 
    int available; 
    int guard; 
    Entry *entries; 
} Map; 

とコードを実装したいと考えていると、これらの構造体があります。私は私のメインの方法

を実行したときに

Map *map_init() { 
    Map *res = (Map *) malloc(sizeof(Map)); 

    res->available = 4; 
    res->guard = 0; 
    res->entries = (Entry *) malloc(4 * sizeof(Entry)); 

    return res; 
} 

int map_put(Map *map, const char *name, int nr) { 
    Entry entry; 
    int i = 0; 

    for (i = 0; i < map->guard; ++i) { 
     entry = map->entries[i]; 
     printf("entry ( x , %u) at %p (%p)\n", entry.number, &entry, entry.name); 

     if (!strcmp(entry.name, name))  // Segmentation fault here 
      return 0; 
    } 

    entry = map->entries[map->guard++]; 
    entry.name = name; 
    entry.number = nr; 

    printf("entry (%s, %u) at %p (%p)\n", entry.name, entry.number, &entry, entry.name); 

    return 1; 
} 

int main(int argc, char **argv) { 
    printf("Initialising...\n"); 
    Map *map = map_init(); 

    printf("Putting...\n"); 
    map_put(map, "test", 2); 
    map_put(map, "some", 1); 

    // ... 

    free(map->entries); 
    free(map); 
    return 0; 
} 

出力として取得する

Initialising... 
Putting... 
entry (test, 2) at 0x7fff50b32a90 (0x10f0cdf77) 
entry ( x , 0) at 0x7fff50b32a90 (0x5000000000000000) 
Segmentation fault: 11 

セグメンテーションフォールトは、entry.nameがもはや文字列を指していないという事実に由来しています(数字も失われますが、これは不正なメモリアクセスにつながりません)。私がmap_putの最初の呼び出しでデータを設定すると、すべてが正しい場所に格納されているように見えます。

誰でも、これらのエントリが上書きされる可能性があり、値が保存されない理由は何ですか?

+0

[mallocの結果のキャストに関するこのディスカッション](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc)を読むことをお勧めします。 –

答えて

3

問題はこれです:ここでは

entry = map->entries[map->guard++]; 

あなたがentry構造インスタンスに配列からのデータをコピーします。次に、entryのデータを変更し、それらの変更を破棄します。配列内の元の構造データは変更されません。

map_putの次の呼び出しで、配列の初期化されていない構造体を使用すると、もちろん、の未定義の動作になります。

アレイ構造のインスタンスを直接変更し、map->guardを別途増やしてください。または、entryポインタにして、配列要素を指すようにします。

1

map_putの変数entryはポインタではありません。それは構造体です。だから、entryにコード

entry = map->entries[map->guard++]; 
entry.name = name; 
entry.number = nr; 

コピーmap->entries[map->guard]の内容は。その後、entryのフィールドを更新し、関数から戻ります。

正しいコードは、あなたがmap_putにおける主要な問題を持っているこの

int map_put(Map *map, const char *name, int nr) { 
    Entry *entry; // <-- entry is a pointer 
    int i = 0; 

    for (i = 0; i < map->guard; ++i) { 
     entry = &map->entries[i]; 
     printf("entry ( x , %u) at %p (%p)\n", entry->number, (void *)entry, (void *)entry->name); 

     if (!strcmp(entry->name, name)) 
      return 0; 
    } 

    entry = &map->entries[map->guard++]; 
    entry->name = name; 
    entry->number = nr; 

    printf("entry (%s, %u) at %p (%p)\n", entry->name, entry->number, (void *)entry, (void *)entry->name); 

    return 1; 
} 
1

のように見えます。ローカルEntryを使用します。コピーのエントリがマップから取得されます。しかし、後でローカルコピーに値を割り当てると、マップの元のエントリは変更されません。

したがって、後で新しい名前と既存のエントリを比較しようとすると、未定義の値(未定義の動作)と比較されます。

代わりEntry *使用する必要があります。

int map_put(Map *map, const char *name, int nr) { 
    Entry *entry; 
    int i = 0; 

    for (i = 0; i < map->guard; ++i) { 
     entry = map->entries + i; 
     printf("entry ( x , %u) at %p (%p)\n", entry->number, entry, entry->name); 

     if (!strcmp(entry->name, name))  // Segmentation fault here 
      return 0; 
    } 

    entry = &map->entries[map->guard++]; 
    entry->name = name; 
    entry->number = nr; 

    printf("entry (%s, %u) at %p (%p)\n", entry->name, entry->number, entry, entry->name); 

    return 1; 
} 

をしかし、それがすべてではありません。文字列のアドレスを名前に格納するだけです。あなたが実際に文字列定数を渡しているので、この例では問題ありません。しかし、標準入力またはファイルから文字列を読み込むと、バッファの内容はそれぞれの新しい値で上書きされます。アドレスを保存するだけで、すべてのエントリが同じ値を指すようになります。最後のエントリが終了します。

IMHO strdupを使用して文字列のコピーを保存し、最後に解放する必要があります。ところで、Mapを初期化するinit関数があるので、すべての必要な空き領域を1つの場所にまとめておく必要があります。

関連する問題