Хранятся ли переменные стека C в обратном порядке?

Я пытаюсь понять, как C выделяет память в стеке. Я всегда думал, что переменные в стеке могут быть изображены как переменные-члены structs, они занимают последовательный, непрерывный блок байтов внутри стека. Чтобы проиллюстрировать эту проблему, которую я где-то нашел, я создал эту небольшую программу, которая воспроизвела это явление.

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

void function(int  *i) {
    int *_prev_int =  (int *) ((long unsigned int) i -  sizeof(int))  ;
    printf("%dn", *_prev_int );    
}

void main(void) 
{
    int x = 152;
    int y = 234;
    function(&y);
}

видишь, что я делаю? Предположим sizeof(int) is 4: я ищу 4 байта за переданным указателем, так как это прочитало бы 4 байта до того, где int y на абонента стек.

он не напечатал 152. Странно, когда я смотрю на следующие 4 байта:

int *_prev_int =  (int *) ((long unsigned int) i +  sizeof(int))  ;

и теперь он работает, печатает то, что в x внутри стека вызывающего абонента. Почему?--4--> имеет меньший адрес, чем y? Хранятся ли переменные стека вверх ногами?

2 ответов


организация стека полностью нет данных и конкретной реализации. На практике это зависит от большого количества компилятора (даже его версии) и флагов оптимизации.

некоторые переменные даже не сидят в стеке (например, потому, что они просто хранятся внутри некоторых регистров или потому, что компилятор оптимизировал их-например, путем вставки, постоянного сворачивания и т. д..).

кстати, у вас может быть некоторая гипотетическая реализация C, которая не использует стек (даже если я не могу назвать такую реализацию).

чтобы понять больше о стеках:

  • читайте wikipage на стеки вызовов, хвост называет, темы и продолжения

  • познакомьтесь сархитектура & набор команд (например, x86) & ABI, затем...

  • попросите компилятор показать код ассемблера и / или некоторые промежуточные представления компилятора. При использовании GCC скомпилировать простой код с gcc -S -fverbose-asm (чтобы получить ассемблерный код foo.s при составлении foo.c) и попробовать несколько уровней оптимизации (по крайней мере -O0, -O1, -O2 ....). Попробуйте также -fdump-tree-all option (он сбрасывает сотню файлов, показывающих некоторые внутренние представления компилятора для вашего исходного кода). Обратите внимание, что GCC также обеспечивает обратный адрес builtins

  • читать старую бумагу Аппеля на сбор мусора может быть быстрее, чем распределение стека, и понял вывоз мусора методы (поскольку им часто нужно проверять и, возможно, изменять некоторые указатели внутри фреймов стека вызовов). Чтобы узнать больше о GC, прочитайте руководство GC.

к сожалению, я не знаю языка низкого уровня (например, C, D, Rust, C++, Go,...) где стек вызовов доступен на уровне языка. Вот почему кодирование сборщика мусора для C сложно (так как GC-s нужно сканировать указатели стека вызовов)... Но смотри!--64-->консервативный GC Бема для очень практичного и прагматичного решения.


почти все архитектуры процессоров в настоящее время поддерживают инструкцию по манипуляции стеком(e.G LDM, STM instructions in ARM). Компиляторы С помощью этих инструментов stack. В большинстве случаев, когда данные помещаются в стек, указатель стека уменьшается (растет вниз) и увеличивается, когда данные появляются из стека.

Так это зависит от архитектуры процессора и компилятора, как реализован стек.