2

私はbison/yaccを学んでおり(そしていくつかのcも見直しています)、単純なテストプロジェクトとしてjsonパーサーを構築しようとしています。

http://www.json.org/にある用語を使用すると、文字列/値のペアを表す構造体ペアと、基本的にペアのリンク リストへのポインターを含む members フィールドを持つオブジェクトを表す構造体オブジェクトがあります。 .

新しいペアを返す単純な c 関数 (create_pair) があります。説明できない奇妙な動作に気付きました。

  • 「メイン」からそのような関数を呼び出し、返された構造体のメモリアドレスを出力すると、それらのアドレスは常に異なります。
  • バイソンの「アクション」内でまったく同じ関数を呼び出すと、関数が常に同じメモリアドレスに常駐するポインターを返すことがわかります。

これは意味がありますか?

詳細/コードは次のとおりです。

コードは次のとおりです(リンクには、「プロジェクト」に含まれる4つの異なるファイルを指す4つのペーストビンリンクのリストが含まれています):

次のようにコンパイルして実行できます。

lex t.l
yacc -d t.y
cc y.tab.c lex.yy.c t.c
./a.out

コードを起動し、次の入力で実行すると:

{ "firstName": "A", "lastName": "B" }

次のことがわかります。

1) 「メイン」で実行されるコード (ファイル ty をチェック) は、4 つの異なるペア オブジェクトを作成します。次に、それらのメモリ アドレスを出力すると、出力は次のようになります (異なるアドレスに注意してください)。

p 0x7fff52476be8 //(<-memory address for pair p)
print pair: P, Hellov
q 0x7fff52476bc8 //(<-memory address for pair q)
print pair: Q, Hellox

2) 上記の json サンプルを貼り付けるとすぐに、"ペア" ルールに 2 回ヒットします。1 回目は "firstName": "A"、2 回目は "lastName": "B" で、新しいペアを作成します。どちらの場合もメモリアドレスを出力し、それらは同じです:

Creating pair 0x7fff52475c88
print pair: firstName, A
Creating pair 0x7fff52475c88
print pair: lastName, B

なぜこれが起こるのですか?

4

2 に答える 2

2

a のアドレスが何であるかを気にする必要はありませんpair。それらで実行されている作業とは無関係であり、表示されているアドレスは偶発的なものであり、結果はありません.

関数create_pairはポインターを返しません。で宣言されてpair create_pair(…)いるため、値によって , を返しpairます。

ではmain、 を定義しますpair p = create_pair(l, v);pこれにより、通常はスタックにスペースを確保することにより、自動オブジェクトが作成されます。次に、 を呼び出しますcreate_pair。によって返される値create_pairは にコピーされpます。後で を印刷すると、返されたアドレスではなく、&pのアドレスが印刷されます。pcreate_pair

同様に、 を定義するpair q = create_pair(l, x);と、別のオブジェクト が作成されますq。このオブジェクトの存続期間は の存続期間と重複するためp、それらは異なる場所にある必要があり、異なるアドレスを持っています。を印刷する&qと、この別のアドレスが表示されます。

次に、Bison ルールに配置したコードについて考えてみましょうpair p = create_pair($<u_string>1, $<u_value>3);。Bison は、ルールの処理中にこのコードを実行します。自動オブジェクトを作成し、そのアドレスを出力します。その後、実行はこのコードの範囲を離れ、Bison は間違いなく他のことを続け、現在実行中の処理を終了します。自動オブジェクトの有効期間が終了し、スタックにあったデータがポップされます。その後、Bison はこのルールに戻ります。その時点で、コンピューターは機械的に動作するため、スタック ポインターは以前と同じアドレスを持ちます。そのため、新しいpが作成されると、たまたま古い と同じ場所にありpます。同時に存在するために別の場所になければならなかった と とは異なり、この古いものpと新しいものqpp異なる時期にのみ存在するため、同じ場所にある可能性があります。

これは必ずしも常に起こるとは限りません。あなたの文法がもっと複​​雑な場合、Bison はある時点で他のものをスタックに持っていて、別のものを持っていないかもしれません (あるいはそうでないかもしれません; Bison が生成する解析マシンはそのように動作しないかもしれません; 私は直接知りません)。または、別のルールに同じコードがある場合、そのルールが処理されたときにスタックが異なる可能性があります。

于 2012-12-23T00:33:37.427 に答える
0

つまり、スタック変数のアドレスが変化しています。これは完全に正常です。それらがすべて同じアドレスを持っている場合、一方の値が他方を上書きすることになり、あまり役に立ちません。

編集:関数を呼び出すとき(メインなどの同じ呼び出し元関数から)、スタック変数のアドレスは常に同じです-呼び出しが行われたときにスタックが最初から同じであるため[通常-もちろん、時にはコンパイラースタックで面白いことをするので、100% 保証されているわけではありません]

Edit2:明確にするために、呼び出しが呼び出しの同じチェーンである場合、たとえば関数Cから関数Bから関数Aを呼び出す場合、BまたはCのどこで呼び出しが行われたかに関係なく、スタックはAで同じであることがよくあります。もちろん、関数 C から関数 D から関数 A を呼び出すと、A 内のローカル変数のアドレスに関してすべての賭けが外れます [まあ、かなり似ている可能性が高いですが、関数 D にいくつかの巨大なローカル変数がある場合は、非常に異なる]。そして、これは典型的なものであるという警告が引き続き適用されます。コンパイラは、すべての呼び出しをクリーンアップするのではなく、「問題が解決するまで」スタックのクリーンアップをそのままにしておく可能性があります。つまり、関数 A への 3 回の呼び出しにより、クリーンアップされない「ガベージ」がスタックに蓄積される可能性があります。後になるまで。

なぜこれと違うべきだと思うのか、少し混乱していますか?

于 2012-12-23T00:26:02.783 に答える