6

これはおそらく、私が今まで遭遇した中で最も奇妙なものの 1 つです。私は C でプログラミングすることはあまりありませんが、オンラインでさまざまな情報源を調べた結果、変数macroNamemacroBodyは while ループのスコープでのみ定義されています。したがって、ループが実行されるたびに、marcoNamemacroBodyが新しいアドレスを取得し、完全に新しい変数になることを期待しています。しかし、そうではありません。

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

アドレスを印刷していて同じなので、これが問題であることはわかっています。

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;
}
4

2 に答える 2

5

これらの配列はスタックに割り当てられます。

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

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

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

コードのアセンブリダンプを見ると、マシンのフレームポインターが、関数の呼び出しスタックがこれらの配列を含むすべてのローカル変数用のスペースを持つのに十分な量だけデクリメントされていることがわかります。

于 2013-03-10T03:11:57.243 に答える
3

chrisaycock の答えに加えて、私が言及する必要があるのは、これらの変数が定義された関数の外部のローカル変数へのポインターを決して使用しないことです。次の例を考えてみましょう。

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コンパイラはパフォーマンス上の理由からこれらのvarを実際に同じアドレスに配置するため、そうではありませんが、それに依存することはできません。

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

于 2013-03-10T09:03:28.600 に答える