1

私は GNU Assembly を使用して、C struct Linked Listed を反復処理し、構造体の値の 1 つから特定の値を見つけようとしています。次のノードに移動して値を確認するために、構造体の *next ポインターにどのようにアクセスするのか疑問に思っています。以下は、試して学ぶために作成したサンプル C コードです。

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

struct node *root;

void functionToBuildLinkList(){
   //Code to malloc and link 4 nodes
 }

int main(int argc, char *argv[]){

   functionToBuildLinkList();

   int valueOne;

   rootPtr = rootPtr->next;
   valueOne = rootPtr->id;
   printf("The valueOne is: %i\n", valueOne);

   return 0;
}

自分でそれを理解するのを助けるために、メインの objdump を調べました。

mov    0x804a024,%eax   //Moving the rootPtr->next address into eax
mov    0x4(%eax),%eax   //Offset 4 bytes for id
mov    %eax,0x804a024   //Compiler nonsense?
mov    0x804a024,%eax   //Compiler nonsense?
mov    (%eax),%eax      //Moving the contents of EAX into EAX or more nonsense?
mov    %eax,0x1c(%esp)  //Moving id into the stack
mov    $0x804861c,%eax  //What address is being moved into EAX?
mov    0x1c(%esp),%edx  //Moving id into edx
mov    %edx,0x4(%esp)   //Moving edx (id) into the stack
mov    %eax,(%esp)      //What's the address in EAX? The top of stack - ESP?
call   8048340 <printf@plt>
mov    $0x0,%eax        //Returning O

コンパイラがハードコードされたメモリアドレスを提供しているように見えるので、私はちょっとうんざりしています。次のメモリアドレスや特定のノードがどこにあるかを知るにはどうすればよいですか? ヒープにメモリを割り当ててリストを作成したとき、それは順番に並べられているので、そのように計算してみることができますか? リストをどのように処理しますか? また、*next ポインターが最初のメンバー変数である場合、構造体の先頭へのメモリ アドレスを取得するときに (それにアクセスするための) オフセットはありませんか? どんな助けでも大歓迎です。

助けてくれてありがとう!したがって、サンプル コードのグローバル ポインター ルートを使用してリストを反復処理するには、次のようにします。

movl root, %eax   //Move root into EAX
movl (%eax), %eax //eax = eax->next
4

2 に答える 2

1

使用される 2 つの異なるアドレッシング モードがあります。

mov    0x804a024,%eax   //Moving the rootPtr (content) into eax
mov    [%eax], %ebx     //access tmp=rootPtr->next

add    0x4, %ebx        // increment tmp by 4 to get the address of &tmp->id

mov    $0x804861c,%eax  // Moving an address to eax

最初の行は、次のように記述されることがよくあります。

mov.l  [0x804a024], %eax

最初の「ナンセンス」は、割り当ての左側に対応します

rootPtr = rootPtr->next;

次の行は最適化される可能性がありますが。

于 2012-10-07T07:19:08.637 に答える
1

これを適切に注釈しましょう...

mov    0x804a024,%eax   // eax = rootPtr (global variable)
mov    0x4(%eax),%eax   // eax = eax->next (offset 4)
mov    %eax,0x804a024   // rootPtr = eax

mov    0x804a024,%eax   // eax = rootPtr
mov    (%eax),%eax      // eax = eax->id (offset 0)
mov    %eax,0x1c(%esp)  // valueOne = eax (local variable, on stack)
mov    $0x804861c,%eax  // eax = "The valueOne is: %i\n" (static string)
mov    0x1c(%esp),%edx  // edx = valueOne
mov    %edx,0x4(%esp)   // put edx on argument stack (position 1, offset 4)
mov    %eax,(%esp)      // put eax on argument stack (position 0, offset 0)
call   8048340 <printf@plt> // call printf(eax, edx) (= printf(string, valueOne))
mov    $0x0,%eax        // return 0

この例では無駄な動きがたくさんあります。軽い最適化モード (例: -O) でコンパイルすると、通常はより単純なコードを取得できます。のような高レベル-O3では、トリッキーな最適化により、コードが非常に理解しにくくなる可能性があります。

作成するアセンブリ コードは、「ハードコードされた」アドレスを使用しないことに注意してください。グローバルを参照する必要がある場合は、グローバルにラベルを付けて、ラベルを介して参照してください。->nextたとえば、 へのアクセスは単にアクセスの問題であることに注意してください0x4(%eax)(ノード ポインタが にあると仮定しますeax)。これは、nextポインターが構造体の先頭から 4 バイトであるためです。

于 2012-10-07T07:57:52.393 に答える