adresy zmiennych lokalnych na stosie

0

Hej
Jak mam funkcję fun1() i fun2() i obie te funkcje mają jakieś zmienne lokalne i te funkcje wywoływane są w kolejności
fun1();
fun2();
to adresy w wirtualnej przestrzeni adresowej procesu będą mniejsze zmiennych lokalnych funkcji fun1() od fun2()?
No bo stos to najpierw wrzucamy zmienne lokalne fun1 a potem fun2, więc adresy fun1 muszą być mniejsze.
Pytam bo jak sprawdzam w online kompilatorach to te same adresy są:

// Example program
#include <iostream>
#include <string>

void fun1()
{
   int x;
   std::cout << &x << std::endl;
}

void fun2()
{
   int y;
   std::cout << &y << std::endl;
}

int main()
{
   fun1();
   fun2();
   return 0;
}

Output:

0x7c25a4bdd6fc
0x7c25a4bdd6fc

A pytanie takie z .upy bo na rozmowach kwalifikacyjnych takie pytania zadają to dobrze wiedzieć:)

6

Po zakończeniu wykonania funkcji zdejmujesz jej zmienne ze stosu, więc następna funkcja w ogóle tego nie odczuje¹

C++ jako taki nawet nie narzuca wykorzystania stosu w ogóle, nie wspominając już o tym czy jest on monolityczny lub rosnący w dół. To detale implementacyjne.

¹ pomijając UB, a w szczególności zależność od niezainicjalizowanych danych.

1
kq napisał(a):

C++ jako taki nawet nie narzuca wykorzystania stosu w ogóle, nie wspominając już o tym czy jest on monolityczny lub rosnący w dół. To detale implementacyjne.

Odpowiedź w małym stopniu zależy od języka a bardziej od architektury komputera. Oczywiście może być język który olewa architekturę i wszystko robi samodzielnie (software'owo)
Może być też tak że architektura jest zbyt uboga i wtedy stos systemowy jest zrobiony w pełni software'owo a nie sprzętowo. Np tak jak w AVR-GCC. Wtedy decydują twórcy kompilatora.
Jeśli dobrze pamiętam studia to np. dla architektury ARM można wybrać czy stos jest z rosnącymi adresami czy malejącymi.

2

TLDR: Wywołanie funkcji odkłada na stos pewne informacje. O tym co dokładnie zostanie odłożone decyduje tzw. calling convention np. cdecl dla języka C czy fastcall/thiscall dla C++.
Generalnie to co się odkłada na stos to argumenty funkcji, adres powrotu, stary adres podstawy stosu (EBP) a następnie zmienne lokalne (plus parę innych rzeczy).

Ponieważ przenosimy początek stosu przez zmianę rejestru EBP to dzięki temu do argumentów i zmiennych lokalnych w wywoływanej funkcji możemy odwoływać się w relacji do EBP e.g. EPP-8 czy EPP+32 - jest to wygodne z punktu widzenia assemblera.

Gdy funkcja się kończy, to najpierw przywraca stan stosu (EBP, ESP) do stanu z przed wywołania, potem wykonuje instrukcję RET która podniesie ze stosu adres powrotu i wykona skok.
Funkcja wywołująca zdejmie po powrocie z instrukcji CALL położone na stosie argumenty (zazwyczaj przesuwając po prostu rejestr ESP).

To tak w dużym uproszczeniu bo trzeba czasami też zadbać o przywrócenie stanu rejestrów i stosu zmiennoprzeciknowego. Jest to bardzo ciekawy i wdzięczny temat radzę zacząć od Wikipedi: https://en.wikipedia.org/wiki/X86_calling_conventions

Widzimy więc że po wywołaniu func1 i powrocie do main stos wrócił to takiego samego stanu jak przed wywołaniem func1. Jeżeli chcesz mieć inne adresy to przekaż jakieś parametry do funkcji - to przesunie zmienne lokalne niżej (stos rośnie w dół).

0

Dlaczego po dodaniu takiej linijki wynik nadal jest taki sam? "x" nie powinien zostać odłożony na stos?

   fun1();
   int x = 5;
   fun2();
1
$ cat foo.c 
#include <stdio.h>
#include <alloca.h>

void fun1() {
    int x;
    printf("&x=%p\n", &x);
}

void fun2() {
    int y;
    printf("&y=%p\n", &y);
}

int main(int argc, char** argv) {
    fun1();
    alloca(16);
    fun2();
    return 0;
}

$ gcc foo.c

$ ./a.out 
&x=0x7fffcdc49014
&y=0x7fffcdc48ff4

U mnie działa :D

1 użytkowników online, w tym zalogowanych: 0, gości: 1