Waarom groeit de C-programmastack niet naar beneden?

Voer het volgende programma uit op gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 en Intel (R) Core (TM) 2 Duo CPU, ik wil controleren of de c-programmastack naar beneden toe groeit, ik schrijf de volgende code:

    #include 
    #include 
    #include 

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

Ik krijg dit resultaat:

    &"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

Ik vind dat de adressen van str1, str2, str3, str4 naar boven groeien, de adressen van p1, p2, p3, p4 ook omhoog groeien, niet naar beneden, waarom?

1
@JimBalter Bedoel je OS?
toegevoegd de auteur Victor S, de bron
@octopusgrabbus Ik gebruik gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3 en Intel (R) Core (TM) 2 Duo CPU
toegevoegd de auteur Victor S, de bron
Het kan handig zijn om de compiler en het platform op een rij te zetten om uw vraag te beantwoorden, maar hier is een leuke link: stackoverflow.com/questions/3572610/stack-growth-direction
toegevoegd de auteur octopusgrabbus, de bron
toegevoegd de auteur Daniel Fischer, de bron
Omhoog of omlaag is zo relatief: wat is "up" want je zou heel goed "down" kunnen zijn voor mensen in Australië :)
toegevoegd de auteur dasblinkenlight, de bron
Zoek SO voor [c] stapelrichting
toegevoegd de auteur Jim Balter, de bron
@VictorS Ik bedoel StackOverflow, de plaats waarnaar je een bericht verzendt.
toegevoegd de auteur Jim Balter, de bron

4 antwoord

De C-standaard zegt niets over een stapel, laat staan ​​in welke richting hij groeit.

Elk gedrag dat u waarneemt, ligt geheel aan uw specifieke compiler (die op zijn beurt zal worden beïnvloed door het specifieke platform waarop u actief bent.)

4
toegevoegd
Ik gebruik gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
toegevoegd de auteur Victor S, de bron
Intel (R) Core (TM) 2 Duo CPU
toegevoegd de auteur Victor S, de bron

Je programma test de richting van de stapel niet.

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

De compiler kan de automatische objecten in de omgekeerde volgorde van weergave in het programma duwen.

Een goede controle om de richting van de stapel te testen (die op sommige platforms echt omhoog gaat), is om de adressen van automatische objecten in twee verschillende functies te controleren, waarbij één functie de andere functie oproept.

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");
}
2
toegevoegd
Ik heb geen andere antwoorden over stapelrichting gelezen, dit is mijn antwoord op deze vraag.
toegevoegd de auteur ouah, de bron
Dit is allemaal behandeld in antwoorden op eerdere vragen over stapelrichting.
toegevoegd de auteur Jim Balter, de bron

Het groeit eigenlijk naar beneden op je platform, wat ik denk dat het Linux + 32 bit x86 is. Er zijn 2 registers die worden gebruikt voor het adresseren van de stapel op het x86-platform, basisaanwijzer (BP) en stapelwijzer (SP). SP wordt automatisch verhoogd als waarden worden gepusht en verlaagd als ze worden gepoft. Voordat een functie wordt aangeroepen, duwt de callee de functie op stapel, in omgekeerde volgorde, waarbij het eerste argument de bovenste op de stapel is.

In de hoofdtekst van de functie verzendt de compiler code die de oorspronkelijke SP in BP opslaat, en vervolgens verhoogt de SP voldoende voor alle lokale variabelen; deze worden over het algemeen in toenemende richting toegewezen en via de BP-aanwijzer binnen de functie-instantie geadresseerd. Met name de lokale variabelen in uw geval worden niet op de stapel "geduwd" omdat ze niet-geïnitialiseerd zijn.

2
toegevoegd

Natuurlijk bevinden lokale variabelen in main zich op hogere adressen in stack dan die in foo , dus je stack groeit naar beneden - je hoeft je geen zorgen te maken :)

Neem de resultaten in overweging bij het uitvoeren van uw code op Mac OS X met GCC-LLVM 4.2.1 om de case voor de compiler-afhankelijke variabele plaatsing te ondersteunen:

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

Op Linux met GCC 4.2.4-1ubuntu4:

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

Hetzelfde gedrag waargenomen met GCC 4.4.3-4ubuntu5.1. Maar met Intel C Compiler v11.0 worden dingen omgekeerd:

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

Het verschil is duidelijk zichtbaar in de assemblagecode die door beide compilers wordt geproduceerd. GCC gebruikt base-based (EBP/RBP) adressering met negatieve offsets (dwz adressering ten opzichte van de bovenkant van het stapelframe), terwijl ICC stack-pointer (ESP/RSP) gebaseerde adressering met positieve offsets gebruikt (dwz adressering ten opzichte van de onderkant van de stack). stapelframe). In beide gevallen heeft p1 de laagste absolute offset, p2 heeft de naast laagste absolute offset enzovoort.

GCC kan ook op stack pointer gebaseerde adressering gebruiken indien de -fomit-frame-pointer optie wordt geleverd (automatisch ingeschakeld met hogere optimalisatieniveaus), maar ten minste GCC tot 4.4.3 behoudt nog steeds de oude variabele lay-out , dwz p1 heeft nu de hoogste offset, p2 heeft de volgende hoogste offset enzovoort. Waarschijnlijk is dat veranderd in nieuwere GCC-versies.

0
toegevoegd