2013-03-10 18 views
6

これはおそらく私が遭遇した中で最も奇妙なものの一つです。私は多くのC言語ではなく、自分が知っていることを知っていて、オンラインでさまざまなソースを調べて、変数マクロ名マクロボディはwhileループのスコープ内でのみ定義されています。したがって、ループが実行されるたびに、私はmarcoNamemacroBodyが新しいアドレスを取得し、まったく新しい変数になると予想しています。しかし、それは真実ではありません。whileループ内の可変スコープ

ループが再び実行されていても、両方の変数が同じアドレスを共有しているため、要素の一意性をチェックする必要があるリンクされたリストの重大な頭痛を引き起こしています。なぜこれがわからないのですか? macroNamemacroBodyは、whileループが実行されるたびに完全に新しいアドレスを取得する必要がありますか?

私はアドレスを印刷しているので、これは問題であることは知っていて、同じです。

while(fgets(line, sizeof(line), fp) != NULL) // Get new line 
{ 
    char macroName[MAXLINE]; 
    char macroBody[MAXLINE]; 

    // ... more code 

    switch (command_type) 
    { 
     case hake_macro_definition: 
      // ... more code 

      printf("**********%p | %p\n", &macroName, &macroBody); 
      break; 

     // .... more cases 
    } 
} 

リンクされたリストコードの一部であるコード。

struct macro { 
    struct macro *next; 
    struct macro *previous; 
    char *name; 
    char *body; 
};  

リンクされたリスト内に要素が既に存在するかどうかをチェックする機能。しかし、* nameは同じアドレスを持っているので、私はいつもif条件の中で終わります。

static struct macro *macro_lookup(char *name) 
{ 
    struct macro *temp = macro_list_head; 

    while (temp != NULL) 
    { 
     if (are_strings_equal(name, temp->name)) 
     { 
      break; 
     }  

     temp = temp->next; 
    } 

    return temp; 
} 
+0

両方の変数に一意のアドレスを取得することを期待している場合は、ポインタを定義してループの実行ごとにメモリを割り当てて解放してみましょう。また、質問のあなたのコメントは面白いです。この場合、スタック変数/メモリポインタであるアドレスの一意性に基づいて決定を下すことに言及します。 – Ganesh

+0

はい、私はおそらくこのようなことをします。ありがとうございました! – ThePedestrian

答えて

5

これらの配列は、スタック上に割り当てられている:

char macroName[MAXLINE]; 
char macroBody[MAXLINE]; 

コンパイラは、あなたの関数の開始時にが存在するあなたのために事前に割り当てられたスペースがあります。言い換えれば、コンピュータの観点から見ると、これらの配列の位置は、関数本体の上部にあるループ本体の外側に定義した場合と同じです。

Cのスコープは、識別子がどこに見えるかを示すだけです。したがって、コンパイラ(コンピュータではない)は、macroNamemacroBodyがループ本体の前または後で参照できないというセマンティクスを強制します。しかし、コンピュータの観点から見ると、これらの配列の実際のデータは、関数が開始されると存在し、関数が終了すると消滅するだけです。

コードのアセンブリダンプを見ると、マシンのフレームポインタが、関数のcall stackがこれらの変数を含むすべてのローカル変数のためのスペースを持つのに十分な大きさだけ減少していることが考えられますアレイ。私はchrisaycockの答えに加えて言及する必要は何

+0

関数が終了すると "新しい"変数しか得られませんが、正しいですか? – ThePedestrian

+2

@ ThePedestrianそれは何か。動的メモリ割り当て、つまり 'malloc() 'を介して"新しい "空間を得ることができます。ところで、このような研究の全分野は、コンピュータの構成*として知られています。 – chrisaycock

+0

私は現在、現時点でコンピュータ組織のクラスに登録されています。あなたが物事をどのように説明したかは、多くの意味があります。私はまったく新しいものではなかった。私は "新しい"メモリ/レジスタが使用されていると仮定しましたが、なぜ変数が1回だけ存在するのかが理解できます。 – ThePedestrian

3

:あなたは、これらの変数が定義された関数の外のローカル変数へのポインタを使用しないでください。この例を考えてみます。

int * f() 
{ 
    int local_var = 0; 
    return &local_var; 
} 
int g(int x) 
{ 
    return (x > 0) ? x : 0; 
} 
int main() 
{ 
    int * from_f = f(); // 
    *from_f = 100; //Undefined behavior 
    g(15); //some function call to change stack 
    printf("%d", *from_f); //Will print some random value 
    return 0; 
} 

同じことは、実際に適用されますブロック。技術的には、ブロックローカル変数は、ブロック終了後に消去することができます。したがって、ループの各反復で古いアドレスは無効になります。 Cコンパイラは実際にパフォーマンスの理由からこれらの変数を同じアドレスに置きますが、それに頼ることはできません。

メモリの割り当て方法を理解する必要があります。リストを実装したい場合、それは成長する構造体です。記憶はどこから来たのですか?スタックから多くのメモリを割り当てることはできません。また、関数から戻るとメモリは無効になります。したがって、ヒープから割り当てる必要があります(mallocを使用)。

+0

ありがとうございます。私は別の問題を抱えており、それはあなたが記述したシナリオから生じています。ありがとう! – ThePedestrian

関連する問題