Рост стека обычно зависит не от самой операционной системы, а от процессора, на котором она работает. Solaris, например, работает на x86 и SPARC. Mac OSX (как вы упомянули) работает на PPC и x86. Linux работает на всем, от моей большой системы System z на работе до маленьких наручных часов .
Если ЦП предоставляет какой-либо выбор, соглашение о вызовах ABI /, используемое ОС, указывает, какой выбор вам нужно сделать, если вы хотите, чтобы ваш код вызывал код всех остальных.
Процессоры и их направление:
x86: вниз.
SPARC: по выбору. Стандартный ABI использует down.
КПП: думаю, да.
System z: в связном списке, я не шучу (но все еще не работает, по крайней мере, для zLinux).
ARM: выбирается, но Thumb2 имеет компактные кодировки только для вниз (LDMIA = приращение после, STMDB = уменьшение до).
6502: вниз (но только 256 байт).
RCA 1802A: как хотите, при условии реализации SCRT.
PDP11: вниз.
8051: вверх.
Показывая мой возраст на последних нескольких, 1802-й был чипом, который использовался для управления ранними шаттлами (я подозреваю, определяя, открыты ли двери, основываясь на вычислительной мощности, которую он имел :-), и моим вторым компьютером, COMX-35 ( вслед за моим ZX80 ).
В архитектуре SPARC используется модель регистров скользящего окна. Архитектурно видимые детали также включают круговой буфер окон регистров, которые действительны и кэшируются внутри, с ловушками при переполнении / недостаточном заполнении. Подробности смотрите здесь . Как объясняется в руководстве по SPARCv8, инструкции SAVE и RESTORE подобны инструкциям ADD плюс ротация окна регистров. Использование положительной константы вместо обычной отрицательной даст растущий вверх стек.
Вышеупомянутый метод SCRT является другим - 1802 использовал несколько или шестнадцать 16-битных регистров для SCRT (стандартный метод вызова и возврата). Один из них был программным счетчиком, вы могли использовать любой регистр в качестве ПК с SEP Rnинструкцией. Один был указателем стека, а два всегда указывали на адрес кода SCRT, один для вызова, один для возврата. Никакой реестр не трактовался особым образом. Имейте в виду, что эти данные взяты из памяти, они могут быть не совсем правильными.
Например, если R3 был ПК, R4 был адресом вызова SCRT, R5 был адресом возврата SCRT, а R2 был «стеком» (кавычки, как это реализовано в программном обеспечении), SEP R4установил бы R4 как ПК и запустил бы SCRT код вызова.
Затем он сохранит R3 в «стеке» R2 (я думаю, что R6 использовался для временного хранения), увеличивая или уменьшая его, захватывает два байта, следующие за R3, загружает их в R3, затем выполняет SEP R3и запускается по новому адресу.
Чтобы вернуться, он должен SEP R5вытащить старый адрес из стека R2, добавить к нему два (чтобы пропустить байты адреса вызова), загрузить его в R3 и SEP R3начать выполнение предыдущего кода.
Поначалу очень сложно осмыслить весь стековый код 6502/6809 / z80, но он все равно элегантен в стиле «удар головой о стену». Также одной из самых продаваемых характеристик чипа был полный набор из 16 16-битных регистров, несмотря на то, что вы сразу потеряли 7 из них (5 для SCRT, два для DMA и прерывания из памяти). Ах, победа маркетинга над реальностью :-)
System z на самом деле очень похожа, используя регистры R14 и R15 для вызова / возврата.
Чтобы добавить в список, ARM может расти в любом направлении, но может быть настроен на одно или другое с помощью конкретной реализации кремния (или может быть оставлен выбираемым программным обеспечением). Те немногие, с которыми я имел дело, всегда находились в режиме роста.
Майкл Берр,
1
В той небольшой части мира ARM, которую я видел до сих пор (ARM7TDMI), стек полностью обрабатывается программным обеспечением. Адреса возврата хранятся в регистре, который при необходимости сохраняется программным обеспечением, а инструкции пре- / пост-инкремента / декремента позволяют помещать его и другие данные в стек в любом направлении.
starblue
1
У HPPA стек вырос! Достаточно редко среди достаточно современных архитектур.
Спасибо @paxdiablo за понимание. Иногда люди воспринимают такой комментарий как личное оскорбление, особенно если он старше. Я знаю только, что есть разница, потому что сам совершал ту же ошибку в прошлом. Береги себя.
(& dummy> addr) не определено. Результат подачи двух указателей на реляционный оператор определяется только в том случае, если два указателя указывают на один и тот же массив или структуру.
sigjuice
2
Попытка исследовать структуру вашего собственного стека - то, что C / C ++ вообще не определяет - изначально «непереносима», так что меня это особо не заботит. Однако похоже, что эта функция будет работать правильно только один раз.
ephemient,
9
Для этого не нужно использовать static. Вместо этого вы можете передать адрес в качестве аргумента рекурсивного вызова.
R .. GitHub ОСТАНОВИТЬ ПОМОЩЬ ICE
5
плюс, используя a static, если вы вызовете это более одного раза, последующие вызовы могут потерпеть неудачу ...
Крис Додд,
7
Преимущество расширения в том, что в старых системах стек обычно располагался наверху памяти. Программы обычно заполняли память, начиная снизу, поэтому такое управление памятью сводило к минимуму необходимость измерения и размещения нижней части стека в разумном месте.
Не в MIPS и многих современных архитектур RISC (например , PowerPC, RISC-V, SPARC ...) есть нет pushи popинструкции. Эти операции явно выполняются путем ручной настройки указателя стека, а затем загрузки / сохранения значения относительно настроенного указателя. Все регистры (кроме нулевого) являются универсальными, поэтому теоретически любой регистр может быть указателем стека, и стек может расти в любом направлении, которое хочет программист.
Тем не менее, стек обычно увеличивается вниз на большинстве архитектур, вероятно, чтобы избежать случая, когда стек и данные программы или данные кучи растут и конфликтуют друг с другом. Также есть веские причины адресации, упомянутые в ответе . Некоторые примеры: MIPS ABI растет вниз и использует $29(AKA $sp) в качестве указателя стека, RISC-V ABI также растет вниз и использует x2 в качестве указателя стека.
В Intel 8051 стек увеличивается, вероятно, потому, что пространство памяти настолько мало (128 байт в исходной версии), что нет кучи, и вам не нужно помещать стек сверху, чтобы он был отделен от растущей кучи снизу
Просто небольшое дополнение к другим ответам, которые, насколько я вижу, не коснулись этого момента:
Если стек растет вниз, все адреса в стеке имеют положительное смещение относительно указателя стека. Нет необходимости в отрицательных смещениях, поскольку они будут указывать только на неиспользуемое пространство стека. Это упрощает доступ к расположениям стека, когда процессор поддерживает адресацию относительно указателя стека.
Многие процессоры имеют инструкции, которые разрешают доступ только с положительным смещением относительно некоторого регистра. К ним относятся многие современные архитектуры, а также некоторые старые. Например, ARM Thumb ABI обеспечивает относительный доступ к стековому указателю с положительным смещением, закодированным в одном 16-битном командном слове.
Если стек вырос вверх, все полезные смещения относительно указателя стека были бы отрицательными, что менее интуитивно понятно и менее удобно. Это также противоречит другим приложениям адресации относительно регистров, например, для доступа к полям структуры.
В большинстве систем стек растет вниз, и моя статья на https://gist.github.com/cpq/8598782 объясняет, ПОЧЕМУ он растет. Это просто: как разместить два растущих блока памяти (кучу и стек) в фиксированном фрагменте памяти? Лучшее решение - поставить их на противоположные концы и дать расти друг другу.
Он растет вниз, потому что в памяти, выделенной для программы, есть «постоянные данные», то есть код самой программы внизу, а затем куча в середине. Вам нужна другая фиксированная точка, из которой можно ссылаться на стек, чтобы вы оставались наверху. Это означает, что стек растет вниз, пока не станет потенциально смежным с объектами в куче.
Ответы:
Рост стека обычно зависит не от самой операционной системы, а от процессора, на котором она работает. Solaris, например, работает на x86 и SPARC. Mac OSX (как вы упомянули) работает на PPC и x86. Linux работает на всем, от моей большой системы System z на работе до маленьких наручных часов .
Если ЦП предоставляет какой-либо выбор, соглашение о вызовах ABI /, используемое ОС, указывает, какой выбор вам нужно сделать, если вы хотите, чтобы ваш код вызывал код всех остальных.
Процессоры и их направление:
Показывая мой возраст на последних нескольких, 1802-й был чипом, который использовался для управления ранними шаттлами (я подозреваю, определяя, открыты ли двери, основываясь на вычислительной мощности, которую он имел :-), и моим вторым компьютером, COMX-35 ( вслед за моим ZX80 ).
Подробности PDP11 почерпнуты отсюда , 8051 подробности отсюда .
В архитектуре SPARC используется модель регистров скользящего окна. Архитектурно видимые детали также включают круговой буфер окон регистров, которые действительны и кэшируются внутри, с ловушками при переполнении / недостаточном заполнении. Подробности смотрите здесь . Как объясняется в руководстве по SPARCv8, инструкции SAVE и RESTORE подобны инструкциям ADD плюс ротация окна регистров. Использование положительной константы вместо обычной отрицательной даст растущий вверх стек.
Вышеупомянутый метод SCRT является другим - 1802 использовал несколько или шестнадцать 16-битных регистров для SCRT (стандартный метод вызова и возврата). Один из них был программным счетчиком, вы могли использовать любой регистр в качестве ПК с
SEP Rn
инструкцией. Один был указателем стека, а два всегда указывали на адрес кода SCRT, один для вызова, один для возврата. Никакой реестр не трактовался особым образом. Имейте в виду, что эти данные взяты из памяти, они могут быть не совсем правильными.Например, если R3 был ПК, R4 был адресом вызова SCRT, R5 был адресом возврата SCRT, а R2 был «стеком» (кавычки, как это реализовано в программном обеспечении),
SEP R4
установил бы R4 как ПК и запустил бы SCRT код вызова.Затем он сохранит R3 в «стеке» R2 (я думаю, что R6 использовался для временного хранения), увеличивая или уменьшая его, захватывает два байта, следующие за R3, загружает их в R3, затем выполняет
SEP R3
и запускается по новому адресу.Чтобы вернуться, он должен
SEP R5
вытащить старый адрес из стека R2, добавить к нему два (чтобы пропустить байты адреса вызова), загрузить его в R3 иSEP R3
начать выполнение предыдущего кода.Поначалу очень сложно осмыслить весь стековый код 6502/6809 / z80, но он все равно элегантен в стиле «удар головой о стену». Также одной из самых продаваемых характеристик чипа был полный набор из 16 16-битных регистров, несмотря на то, что вы сразу потеряли 7 из них (5 для SCRT, два для DMA и прерывания из памяти). Ах, победа маркетинга над реальностью :-)
System z на самом деле очень похожа, используя регистры R14 и R15 для вызова / возврата.
источник
В C ++ (адаптируется к C) stack.cc :
источник
static
. Вместо этого вы можете передать адрес в качестве аргумента рекурсивного вызова.static
, если вы вызовете это более одного раза, последующие вызовы могут потерпеть неудачу ...Преимущество расширения в том, что в старых системах стек обычно располагался наверху памяти. Программы обычно заполняли память, начиная снизу, поэтому такое управление памятью сводило к минимуму необходимость измерения и размещения нижней части стека в разумном месте.
источник
Стек растет вниз на x86 (определяется архитектурой, выталкивает указатель стека на приращение, нажимает на уменьшение).
источник
Не в MIPS и многих современных архитектур RISC (например , PowerPC, RISC-V, SPARC ...) есть нет
push
иpop
инструкции. Эти операции явно выполняются путем ручной настройки указателя стека, а затем загрузки / сохранения значения относительно настроенного указателя. Все регистры (кроме нулевого) являются универсальными, поэтому теоретически любой регистр может быть указателем стека, и стек может расти в любом направлении, которое хочет программист.Тем не менее, стек обычно увеличивается вниз на большинстве архитектур, вероятно, чтобы избежать случая, когда стек и данные программы или данные кучи растут и конфликтуют друг с другом. Также есть веские причины адресации, упомянутые в ответе . Некоторые примеры: MIPS ABI растет вниз и использует
$29
(AKA$sp
) в качестве указателя стека, RISC-V ABI также растет вниз и использует x2 в качестве указателя стека.В Intel 8051 стек увеличивается, вероятно, потому, что пространство памяти настолько мало (128 байт в исходной версии), что нет кучи, и вам не нужно помещать стек сверху, чтобы он был отделен от растущей кучи снизу
Дополнительную информацию об использовании стека в различных архитектурах можно найти в https://en.wikipedia.org/wiki/Calling_convention.
Смотрите также
источник
Просто небольшое дополнение к другим ответам, которые, насколько я вижу, не коснулись этого момента:
Если стек растет вниз, все адреса в стеке имеют положительное смещение относительно указателя стека. Нет необходимости в отрицательных смещениях, поскольку они будут указывать только на неиспользуемое пространство стека. Это упрощает доступ к расположениям стека, когда процессор поддерживает адресацию относительно указателя стека.
Многие процессоры имеют инструкции, которые разрешают доступ только с положительным смещением относительно некоторого регистра. К ним относятся многие современные архитектуры, а также некоторые старые. Например, ARM Thumb ABI обеспечивает относительный доступ к стековому указателю с положительным смещением, закодированным в одном 16-битном командном слове.
Если стек вырос вверх, все полезные смещения относительно указателя стека были бы отрицательными, что менее интуитивно понятно и менее удобно. Это также противоречит другим приложениям адресации относительно регистров, например, для доступа к полям структуры.
источник
В большинстве систем стек растет вниз, и моя статья на https://gist.github.com/cpq/8598782 объясняет, ПОЧЕМУ он растет. Это просто: как разместить два растущих блока памяти (кучу и стек) в фиксированном фрагменте памяти? Лучшее решение - поставить их на противоположные концы и дать расти друг другу.
источник
Он растет вниз, потому что в памяти, выделенной для программы, есть «постоянные данные», то есть код самой программы внизу, а затем куча в середине. Вам нужна другая фиксированная точка, из которой можно ссылаться на стек, чтобы вы оставались наверху. Это означает, что стек растет вниз, пока не станет потенциально смежным с объектами в куче.
источник
Этот макрос должен обнаруживать его во время выполнения без UB:
источник