2017-09-29 10 views
1

私は、単純なリンクリストポインタ - メモリロケーションへの一貫性のないアクセス

#include <stdio.h> 


struct node{ 
    int value; 
    struct node* next; 
}; 


typedef struct { 
    struct node* root; 
} ll; 

void add_to_ll(int value, ll* linked_list) { 
    struct node new_node = {value, linked_list->root}; 
    linked_list->root = &new_node; 
} 

void print_ll(ll* ll2) { 
    printf("%p", ll2); 
    struct node* temp = ll2->root; 
    while (temp->next != NULL) { 
    printf("%d ", temp->value); 
    temp = temp->next; 
    } 
} 


int main() 
{ 
    printf("Creating a linked list...\n"); 
    struct node root_node = {1, NULL}; 
    ll my_linked_list = { &root_node }; 
    for (int i = 0; i < 10000; i++) { 
    add_to_ll(i, &my_linked_list); 
    } 
    printf("my_linked_list root value %d\n", my_linked_list.root->value); 
    printf("my_linked_list root value %d\n", my_linked_list.root->value); 
    printf("my_linked_list root value %d\n", my_linked_list.root->value); 
    return 0; 
} 

を定義しようとしています、私は取得しています出力は次のようになります。

Creating a linked list... 
my_linked_list root value 9999 
my_linked_list root value 429391991 
my_linked_list root value 429391991 

私はrootvalueを取得することができていますノードを正しく最初に起動します。しかし、それを2度目(それ以後)に読もうとすると値が変化します。私は何が欠けていますか?

+0

'add_to_ll'はメモリを割り当てません。あなたのリストにぶら下がっているポインタを追加しています(終了するまで、一時的にそれを割り当てています)。そしてあなたは*未定義の動作*の素敵なイラストを見ています。 –

+0

'new_node'に割り当てられたメモリは' add_to_ll'が返るまでしか使えないので、そのメモリへのポインタを返すことは未定義の動作です。 'malloc'を使用して新しいノードをどこか長く持続させる必要があります。 – ikegami

+0

[参考文献](https://stackoverflow.com/questions/13415321/difference-between-static-auto-global-and-local-variable-in-the-context-of-ca) – ikegami

答えて

0

この機能:

void add_to_ll(int value, ll* linked_list) { 
    struct node new_node = {value, linked_list->root}; 
    linked_list->root = &new_node; 
} 

がスタックにノードを作成し、リストに追加します。 この関数から戻ると、スタックがポップされ、リストルートはスタック上の未割り当てメモリを指し、後で再利用されます。

+0

スタックが使用されます。重要な部分は、メモリがブロックの終わり(すなわち、関数が戻るまで)にのみ 'new_node'のために予約されていることです。 ///この問題に対する解決策を提供していれば、この回答は良いでしょう。 – ikegami

+0

標準の約束(場所については何も言わずに限られた生涯)と30年後に働いたすべてのコンパイラが実際に何をしているのか(スタック上に構造体のためのスペースを割り当てる)と、彼が最初にこのコードを書いたかどうかを理解するためのOP。スタック上の構造について話すことは、概念を伝える最も簡単な方法です。 –

+0

正確性を忘れても、無関係の詳細を追加してもOPの理解には役立ちません。保管場所にはまったく言及する必要はありません。 – ikegami

2

あなたの問題はaddのメモリ割り当て戦略にあります。

void add_to_ll(int value, ll* linked_list) { 
    struct node new_node = {value, linked_list->root}; 
    linked_list->root = &new_node; 
} 

ここでは、new_nodeをローカル変数として設定しています。非静的ローカル変数の寿命は、ブロックの寿命と同じです。ブロックを終了すると、そのメモリ(実際にはスタック)は、オブジェクトを上書きする連続的な割り当てに使用できます。明示的な割り当て、すなわちmallocを使用すると、その寿命が割り当ての範囲から独立しているオブジェクトを持つことができます。

私はまた、命名を指摘します...最も明示的な名前は、変数ではなくタイプのものでなければなりません。だからstruct linked_list llで、それ以外の方法ではありません。

+0

問題を解決するために使用できるいくつかの可能なテクニックの1つは 'malloc()'ですが、生涯の問題を言わずにOPを使用するように指示すると、定義されていない動作の問題。そして、あなたの「命名」のコメントは、この質問の話題とは全く関係ありません。[そこに - 私は気分が良く、私は、この答えの前に、そして問題[笑い]のコメントの前に投稿されていない場合、私の、より簡単で、より明確かつ実用的に、pedantically正しい答えが投稿されたことを言及する必要はありません –

+0

私はOPがCプログラミングの初心者であると仮定して答えます。それで、私はローカル配分について豊富に話し、命名を指摘し、単にmalloc()と名づけたので、OPの勉強の次のステップを示唆し、エクササイズを呼び出す前にメモリリークについて知ることを望みます。私はいつも不要な部分を取り除き、作業と安全なコードを提供することができましたが、それはOPを犠牲にして問題に役立ちます – rzippo

関連する問題