9

私は次のプログラムを持っています。次の64ビットマシンでなぜ-4を出力するのだろうか?私の仮定のどれが間違っていましたか?

[Linux ubuntu 3.2.0-23-generic#36-Ubuntu SMP Tue Apr 10 20:39:51 UTC 2012 x86_64 x86_64 x86_64 GNU / Linux]

  1. 上記のマシンとgccコンパイラでは、デフォルトでbを最初と2番目にプッシュする必要があります。スタックは下向きに成長します。したがって、bのアドレスは高く、aのアドレスは低くする必要があります。したがって、結果はポジティブになるはずです。しかし、私は-4を得ました。誰かがこれを説明できますか?

  2. 引数は、スタックフレームで2バイトを占める2文字です。しかし、私は違いを4と見なしましたが、予想どおり1です。誰かがアライメントのせいであると言っても、2文字の構造は4バイトでアライメントされていないのではないかと思います。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void CompareAddress(char a, char b)
{
    printf("Differs=%ld\n", (intptr_t )&b - (intptr_t )&a);
}

int main()
{
    CompareAddress('a','b');
    return 0; 
}

/* Differs= -4 */
4

3 に答える 3

9

これが私の推測です:

Linux の x64 では、呼び出し規約で、最初のいくつかのパラメーターはレジスターによって渡されると規定されています。

したがって、あなたの場合、 と の両方abスタックではなくレジスターによって渡されます。ただし、そのアドレスを取得するため、関数が呼び出された後、コンパイラはそれをスタックのどこかに格納します。
(下から順に不要です。)

関数が完全にインライン化されている可能性もあります。

いずれの場合も、コンパイラは変数を格納するための一時的なスタック スペースを作成します。これらは任意の順序にすることができ、最適化の対象となります。そのため、期待する特定の順序ではない場合があります。

于 2012-06-16T03:07:20.353 に答える
5

このような (特定のプラットフォームでの特定のコンパイラの動作に関する) 質問に答える最善の方法は、アセンブラを調べることです。gccフラグを渡すことで、そのアセンブラをダンプすることができます-S(-fverbose-asmフラグも素晴らしいです)。ランニング

gcc -S -fverbose-asm file.c

少し似た aが得られfile.sます (無関係なビットはすべて削除しました。括弧内のビットは私のメモです):

CompareAddress:
        # ("allocate" memory on the stack for local variables)
        subq    $16, %rsp       
        # (put a and b onto the stack)
        movl    %edi, %edx      # a, tmp62
        movl    %esi, %eax      # b, tmp63
        movb    %dl, -4(%rbp)   # tmp62, a
        movb    %al, -8(%rbp)   # tmp63, b 
        # (get their addresses)
        leaq    -8(%rbp), %rdx  #, b.0
        leaq    -4(%rbp), %rax  #, a.1
        subq    %rax, %rdx      # a.1, D.4597 (&b - &a)
        # (set up the parameters for the printf call)
        movl    $.LC0, %eax     #, D.4598
        movq    %rdx, %rsi      # D.4597,
        movq    %rax, %rdi      # D.4598,
        movl    $0, %eax        #,
        call    printf  #

main:
        # (put 'a' and 'b' into the registers for the function call)
        movl    $98, %esi       #,
        movl    $97, %edi       #,
        call    CompareAddress

(この質問[re]bpは、何が何であるかをうまく説明してい[re]spます。)

差が負である理由は、スタックが下向きに成長するためです。つまり、2 つのものをスタックにプッシュすると、最初にプッシュした方がアドレスが大きくなり、a前にプッシュされbます。

-4コンパイラが引数を 4 バイト境界に揃えることが「より良い」と判断したのではなく、その理由は-1、おそらく 32 ビット/64 ビット CPU が 1 バイトを処理するよりも 4 バイトを一度に処理するためです。

(また、アセンブラを見ると、その効果が示されます-mpreferred-stack-boundary。これは、基本的に、スタック上のメモリが異なるサイズのチャンクに割り当てられることを意味します。)

于 2012-06-16T03:40:50.257 に答える
0

プログラムがあなたに与えた答えは正しいと思います。GCCのデフォルトの優先スタック境界は4です-mpreferred-stack-boundary=num。GCCオプションに設定してスタック境界を変更すると、プログラムはセットに応じて異なる答えを返します。

于 2012-06-16T03:28:57.297 に答える