2015-02-17 2 views
6

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

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

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

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

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

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

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

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

+0

стек хранится вниз – Ajit

+2

Я думаю, что это реализация определена/или неуказана. Проверьте этот ответ http://stackoverflow.com/a/4105123/1673391 –

+3

Это полностью зависит от платформы, но многие основные платформы фактически увеличивают кучу вверх от раздела кода/данных и стека вниз от вершины (доступно) Память. Вы должны ** никогда не полагаться на это на свой код C, хотя ... – DevSolar

ответ

10

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

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

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

Чтобы понять больше о стеков:

  • Прочитайте вики страницы на call stacks, tail calls, threads, и на continuations

  • Знакомиться с вашего компьютера architecture & instruction set (например x86) & ABI, затем ...

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

  • прочитанного Аппель на garbage collection can be faster than stack allocation, и понять garbage collection метод (так как они часто нужно проверять и, возможно, изменить некоторые указатели внутри фреймов стеки вызовов). Чтобы узнать больше о GC, прочитайте GC handbook.

К сожалению, я не знаю язык низкого уровня (например, C, D, Rust, C++, Go, ...), где стек вызовов доступен на уровне языка.Вот почему кодирование сборщика мусора для C затруднено (так как GC необходимо сканировать указатели стека вызовов) ... Но см. Boehm's conservative GC для очень практичного и прагматичного решения.

+0

ему необходимо указать стандарт для такого утверждения/комментариев. –

+4

AFAIK, стандарт C99 не упоминает/любой стек нормативным образом. –

+1

@GrijeshChauhan: Нет, он не нуждается в кавычках из стандарта, потому что он не указан ** стандартом. Вот почему вы не должны полагаться на свое поведение. – DevSolar

3

Практически все архитектуры процессоров в настоящее время поддерживают инструкцию по управлению стеком (например, LDM, инструкции STM в ARM). Компиляторы с помощью этих инструментов. В большинстве случаев, когда данные вставляются в стек, уменьшаются указатели стека (Growing Downwards) и Increments при вводе данных из стека.

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