1

gcc(Ubuntu / Linaro 4.6.3-1ubuntu5)4.6.3およびIntel(R)Core(TM)2 Duo CPUで次のプログラムを実行し、cプログラムスタックが下向きに成長することを確認したいので、次のコードを記述します。

    #include <stdio.h>
    #include <malloc.h>
    #include <string.h>

    static int a = 1;
    static int b;
    int c = 2;
    int d;

    void foo(void)
    {
        int *p1;
        int *p2;
        int *p3;
        int *p4;

        printf("&p1\t%p\n", &p1);
        printf("&p2\t%p\n", &p2);
        printf("&p3\t%p\n", &p3);
        printf("&p4\t%p\n", &p4);
    }

    int main()
    {
        static int e = 3;
        static int f;
        int g = 4;
        int h;

        char *str1 = "abc";
        char *str2 = "abc";
        char *str3;
        char *str4;

        printf("&\"abc\"\t%p\n", &"abc");
        printf("&str1\t%p\n", &str1);
        printf("&str2\t%p\n", &str2);
        printf("str1\t%p\n", str1);
        printf("str2\t%p\n", str2);

        printf("&str3\t%p\n", &str3);
        printf("str3\t%p\n", str3);

        str4 = (char *)malloc(strlen("abc")*sizeof(char));
        printf("&str4\t%p\n", &str4);
        printf("str4\t%p\n", str4);

        printf("&g\t%p\n", &g);
        printf("&h\t%p\n", &h);

        foo();

        return 0;
    }

私はこの結果を得る:

    &"abc"  0x8048680
    &str1   0xbff1be20
    &str2   0xbff1be24
    str1    0x8048680
    str2    0x8048680
    &str3   0xbff1be28
    str3    0x8048599
    &str4   0xbff1be2c
    str4    0x950f008
    &g  0xbff1be18
    &h  0xbff1be1c
    &p1 0xbff1bde0
    &p2 0xbff1bde4
    &p3 0xbff1bde8
    &p4 0xbff1bdec

str1、str2、str3、str4のアドレスが上向きに成長し、p1、p2、p3、p4のアドレスも下向きではなく上向きに成長するのはなぜですか?

4

4 に答える 4

5

C標準は、スタックについて何も述べていません。ましてや、スタックがどの方向に成長するかについては何も述べていません。

観察する動作はすべて、特定のコンパイラに完全に依存します(これは、実行している特定のプラットフォームの影響を受けます)。

于 2012-06-24T11:52:08.440 に答える
3

プログラムはスタックの方向をテストしません。

    int *p1;
    int *p2;
    int *p3;
    int *p4;

コンパイラーは、プログラムに表示されるのとは逆の順序で自動オブジェクトをプッシュできます。

スタックの方向をテストするための良いチェック(一部のプラットフォームでは実際に上向きになります)は、2つの異なる関数(一方の関数がもう一方の関数を呼び出す)の自動オブジェクトのアドレスをチェックすることです。

void f(void)
{
    int a = 0;
    g(&a);
}

void g(int *p)
{
    int a = 0;

    if (p - &a > 0) printf("stack goes upward\n");
    else  printf("stack goes downard\n");
}
于 2012-06-24T11:57:29.487 に答える
2

実際、Linux+32ビットx86であると私が推測するプラットフォームでは下向きに成長します。x86プラットフォームでスタックをアドレス指定するために使用されるレジスタには、ベースポインタ(BP)とスタックポインタ(SP)の2つがあります。SPは、値がプッシュされると自動的にインクリメントされ、値がポップされるとデクリメントされます。関数が呼び出される前に、呼び出し先は逆の順序で関数をスタックにプッシュします。最初の引数はスタックの一番上の引数です。

ただし、関数本体では、コンパイラは元のSPをBPに格納するコードを出力し、すべてのローカル変数をカバーするのに十分なSPをインクリメントします。これらは通常、増加する方向に割り当てられ、関数本体内のBPポインターを介してアドレス指定されます。特に、あなたの場合のローカル変数は初期化されていないため、スタックに「プッシュ」されません。

于 2012-06-24T12:03:22.263 に答える
0

明らかに、のローカル変数はmainスタック内のアドレスよりも上位のアドレスに配置されているfooため、スタックは下向きに大きくなります。心配する必要はありません:)

コンパイラに依存する変数配置のケースをサポートするには、GCC-LLVM4.2.1を使用してMacOSXでコードを実行したときの結果を考慮してください。

&p1     0x7fff63a5dbf8  ^
&p2     0x7fff63a5dbf0  |
&p3     0x7fff63a5dbe8  |
&p4     0x7fff63a5dbe0  |

LinuxでGCC4.2.4-1ubuntu4を使用する場合:

&p1     0x7fffa1e6d7b8  ^
&p2     0x7fffa1e6d7b0  |
&p3     0x7fffa1e6d7a8  |
&p4     0x7fffa1e6d7a0  |

GCC4.4.3-4ubuntu5.1で観察されたのと同じ動作。しかし、インテルCコンパイラv11.0では、状況が逆転します。

&p1     0x7fff59a84c20  |
&p2     0x7fff59a84c28  |
&p3     0x7fff59a84c30  |
&p4     0x7fff59a84c38  v

違いは、両方のコンパイラによって生成されたアセンブリコードにはっきりと表れています。GCCは、負のオフセットを使用したベースポインタ(EBP / RBP)ベースのアドレス指定(つまり、スタックフレームの上部を基準としたアドレス指定)を使用しますが、ICCは、正のオフセットを使用したスタックポインタ(ESP / RSP)ベースのアドレス指定(つまり、スタックフレームの下部を基準としたアドレス指定)を使用します。スタックフレーム)。どちらの場合p1も、絶対オフセットが最もp2低く、次に絶対オフセットが最も低くなります。

オプションが提供されている場合、GCCはスタックポインタベースのアドレス指定を使用することもでき-fomit-frame-pointerます(より高い最適化レベルで自動的にオンになります)が、少なくとも4.4.3までのGCCは古い変数レイアウトを保持します。つまり、p1現在は最大のオフセットをp2持ち、次に高いオフセットを持ちます。等々。おそらくそれは新しいGCCバージョンで変更されました。

于 2012-06-24T13:13:49.503 に答える