Здравствуйте, Pavel Dvorkin, Вы писали:
PD>За все компляторы и платформы, конечно, не скажу, ограничусь x86. Но, думаю, в других системах то же.
PD>Когда все массивы статические, суммарный объем данных , размещаемых на стеке, при входе в функцию известен до ее выполнения. Поэтому компилятор просто строит код, изменяющий значение регистра стека на суммарный объем. Кстати, по крайней мере VC++ делает это именно при входе в функцию, а не при входе в блок, как вроде бы требовалось. Поэтому в пределах функции значение регистра стека не меняется (конечно, оно изменится, если внутри этой функции войти в другую, но тогда в другой будет то же самое, а выйдем — вернемся в исходное состояние вызываюшей функции). Поэтому адреса всех стековых переменных (и массивов, и скаляров) можно определить в момент входа в функцию и они в ней меняться не будут. В x86 для этого используется базирование по регистру EBP.
Именно! Именно ebp!
PD>Если допустить, что память может внутри функции выделяться на переменный размер, то от этой простой схемы ничего не останется. Придется существенно усложнять код выделения, а значит, упадет скорость. Выгода не перекрывает проблем.
Указатель на текущий фрейм стека сохраняется в ebp. Но выделение массива на стеке его не затрагивает, это выделение делается изменением регистра esp. Ну то есть адресация всех локальных переменных внутри функции никак не изменяется, она не зависит от esp, так как базой служит ebp, а на этот регистр стековые массивы никак не влияют. Никаких там падений скорости нет, код доступа к локальным переменным даже не меняется.
Здравствуйте, x-code, Вы писали:
XC>А почему нельзя как-то вынести на уровень языка динамическое добавление и извлечение объектов из системного стека? Многие алгоритмы таковы, что нужно насоздавать какое-то заранее неизвестное количество объектов из входных данных, и затем их обработать.
А по-твоему как функция printf работает? Поддержка переменного числа аргументов в функциях так и реализована в языке с самого начала.
XC>>А почему нельзя как-то вынести на уровень языка динамическое добавление и извлечение объектов из системного стека? Многие алгоритмы таковы, что нужно насоздавать какое-то заранее неизвестное количество объектов из входных данных, и затем их обработать. WM>А по-твоему как функция printf работает? Поддержка переменного числа аргументов в функциях так и реализована в языке с самого начала.
тем не менее в Си неполноценная поддержака этого.
Скажем, ты не можешь вызвать функцию с переменным и *динамически* определяемым кол-вом аргументов
Здравствуйте, Muxa, Вы писали: M>Почему нельзя сделать вот так?
Как выше уже отметили, в C99 можно. Почему же нельзя в C++ — тому две причины.
1. Для int, вроде, никаких проблем, как и для других POD, а вот насчёт классов с деструктором не уверен. Особенно учитывая исключения.
2. Если в функции есть массив переменного размера, то будет, как заметил Pavel Dvorkin, небольшой оверхед (даже если его не использовать).
В то же время, потребности полностью удовлетворяются vector и scoped_array (возможно, с пользовательским аллокатором). Не менее (если не более) эффективно. Зачем вводить в язык синтаксическую конструкцию, если задачу можно решить существующими средствами?
Здравствуйте, Pzz, Вы писали: Pzz>Ой, да ладно. На входе сохраняем указатель стека в другом регистре и декрементируем его по мере надобности (напомню, стек на x86 растет вниз). На выходе восстанавливаем указатель стека из регистра. Команды enter/leave делают это автоматически.
Здравствуйте, gegMOPO4, Вы писали:
MOP>Здравствуйте, watch-maker, Вы писали: WM>>Указатель на текущий фрейм стека сохраняется в ebp.
MOP>А ведь можно было бы и не сохранять. Вот на этом и оверхед.
Ну детский сад прямо. Этот оверхед — две инструкции. И во-первых, фреймы стека и так много где используются, независимо от поддержки VLA. Во вторых, компилятору ничего не мешает не включать фреймы, если в конкретной функции нет VLA. В-третьих, это всё равно в тысячи раз быстрее, чем выделение массива в куче.
Здравствуйте, watch-maker, Вы писали: WM>Здравствуйте, gegMOPO4, Вы писали: MOP>>Здравствуйте, watch-maker, Вы писали: WM>>>Указатель на текущий фрейм стека сохраняется в ebp. MOP>>А ведь можно было бы и не сохранять. Вот на этом и оверхед. WM>Ну детский сад прямо. Этот оверхед — две инструкции. И во-первых, фреймы стека и так много где используются, независимо от поддержки VLA. Во вторых, компилятору ничего не мешает не включать фреймы, если в конкретной функции нет VLA. В-третьих, это всё равно в тысячи раз быстрее, чем выделение массива в куче.
Не в тысячи, а всего лишь в 2-3 раза (или около того), если использовать специальный аллокатор. Притом в момент запроса (всё равно инициализация массива съест больше), а не при входе в функцию (даже если массив не понадобился).
И если практически того же самого легко добиться самому, то зачем менять язык? Он и так сложный.
Здравствуйте, gegMOPO4, Вы писали:
MOP>Здравствуйте, Pzz, Вы писали: Pzz>>Ой, да ладно. На входе сохраняем указатель стека в другом регистре и декрементируем его по мере надобности (напомню, стек на x86 растет вниз). На выходе восстанавливаем указатель стека из регистра. Команды enter/leave делают это автоматически.
MOP>А всё это лишние команды, лишние такты.
Указатель на стек на входе все равно удобно иметь под рукой, чтобы добираться до параметров. Большинство компиляторов все равно генерируют enter/leave (или эквиавалент), независимо от наличия alloca() или массивов переменной длинны
Здравствуйте, Pzz, Вы писали: Pzz>Здравствуйте, gegMOPO4, Вы писали: MOP>>Здравствуйте, Pzz, Вы писали: Pzz>>>Ой, да ладно. На входе сохраняем указатель стека в другом регистре и декрементируем его по мере надобности (напомню, стек на x86 растет вниз). На выходе восстанавливаем указатель стека из регистра. Команды enter/leave делают это автоматически. MOP>>А всё это лишние команды, лишние такты. Pzz>Указатель на стек на входе все равно удобно иметь под рукой, чтобы добираться до параметров. Большинство компиляторов все равно генерируют enter/leave (или эквиавалент), независимо от наличия alloca() или массивов переменной длинны
Указатель на стек всё равно есть в подавляющем большинстве современных платформ (у которых вообще есть стек). Обращаться по константному смещению от вершины стека одинаково удобно, независимо от количества локальных переменных. От дорогих enter/leave компиляторы избавляются при включенной оптимизации (особенно от конкретно enter/leave).
Здравствуйте, gegMOPO4, Вы писали:
MOP>В то же время, потребности полностью удовлетворяются vector и scoped_array (возможно, с пользовательским аллокатором). Не менее (если не более) эффективно. Зачем вводить в язык синтаксическую конструкцию, если задачу можно решить существующими средствами?
Зачем вообще высокоуровневые языки, когда абсолютно все, что они могут, можно реализовать на асме?
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Здравствуйте, Pavel Dvorkin, Вы писали:
PD>В x86 для этого используется базирование по регистру EBP.
Может использоваться базирование по EBP, для отладки; а так: omit frame pointer и вперед.
PD>Если допустить, что память может внутри функции выделяться на переменный размер, то от этой простой схемы ничего не останется. Придется существенно усложнять код выделения, а значит, упадет скорость. Выгода не перекрывает проблем.
Никто не требует размещать переменные в стеке в порядке их объявления и никто не запрещает размещать динамические массивы в стеке после всех остальных переменных.
Здравствуйте, watch-maker, Вы писали:
XC>>А почему нельзя как-то вынести на уровень языка динамическое добавление и извлечение объектов из системного стека? Многие алгоритмы таковы, что нужно насоздавать какое-то заранее неизвестное количество объектов из входных данных, и затем их обработать. WM>А по-твоему как функция printf работает? Поддержка переменного числа аргументов в функциях так и реализована в языке с самого начала.
Я говорю о прямом ручном размещении объкетов в стеке, через push, и извлечении через pop. Да, это небезопасно, можно случайно повредить стековые переменные или адрес возврата, но ведь на то он и Си чтобы быть небезопасным, да и чтобы повредить переменные есть гораздо более простые способы
Здравствуйте, gegMOPO4, Вы писали:
MOP>Указатель на стек всё равно есть в подавляющем большинстве современных платформ (у которых вообще есть стек). Обращаться по константному смещению от вершины стека одинаково удобно, независимо от количества локальных переменных. От дорогих enter/leave компиляторы избавляются при включенной оптимизации (особенно от конкретно enter/leave).
У большинства, кстати, указателя стека именно что нету. В качестве такового используется регистр общего назначения.
Компилятор избавляется от возни со стеком, если функция достаточно "простая" для этого. Наличие или отсутствие alloca или эквиавалента — лишь еще один критерий простоты.
Здравствуйте, watch-maker, Вы писали:
PD>>Если допустить, что память может внутри функции выделяться на переменный размер, то от этой простой схемы ничего не останется. Придется существенно усложнять код выделения, а значит, упадет скорость. Выгода не перекрывает проблем. WM>Указатель на текущий фрейм стека сохраняется в ebp. Но выделение массива на стеке его не затрагивает, это выделение делается изменением регистра esp. Ну то есть адресация всех локальных переменных внутри функции никак не изменяется, она не зависит от esp, так как базой служит ebp, а на этот регистр стековые массивы никак не влияют.
Отлично. Раскажи, как ты будешь получать доступ к локальным переменым через EBP, если при входе в функцию размещается два динамических массива. Они же тоже локальные переменные, к ним тоже надо добраться
PD>{
PD> int x;
PD> int y[n];
PD> {
PD> int z;
PD> int t[m];
PD>
PD>Кто тут после кого ?
push ebp
mov ebp,esp
sub esp,8
...
mov [ebp-4],eax ; x
mov [ebp-8],eax ; z
...
sub esp,N ; N=4*n
...
sub esp,M ; M=4*m
...
mov [ebp-4],eax ; по прежнему x
mov [ebp-8],eax ; по прежнему z
; т. о. имеем синтаксический оверхед только для самих динамических массивов
...
Проверял без фанатизма, но первый взгляд показал, что именно так — вполне логично как по мне — поступает MSVC++ при использовании alloca (выделяет переменные известного размера до переменного участка и использует ebp даже при указании omit frame pointers). В общем никакой сложности и оверхеда, ну разве что frame pointer теперь не можем опустить.