void
f
()
{
int a[1];
int b;
int c;
int d[1];
}
Я обнаружил, что эти локальные переменные в этом примере не помещаются в стек по порядку. b и c помещаются в порядке их объявления, но a и d группируются вместе. Таким образом, компилятор выделяет массивы иначе, чем любой другой встроенный тип или объект.
Это требование C/C++ или деталь реализации gcc?
Стандарт C ничего не говорит о порядке размещения локальных переменных. Он даже не использует слово «стек». Требуется только, чтобы локальные переменные имели время жизни, которое начинается при входе в ближайший охватывающий блок (в основном, когда выполнение достигает
{
) и заканчивается при выходе из этого блока (достижение}
), и чтобы каждый объект имеет уникальный адрес. Он признает, что две несвязанные переменные могут быть соседними в памяти (по неясным техническим причинам, связанным с арифметикой указателей), но не говорит, когда это может произойти.Порядок, в котором размещаются переменные, полностью зависит от прихоти компилятора, и вы не должны писать код, зависящий от какого-либо конкретного порядка. Компилятор может располагать локальные переменные в том порядке, в котором они объявлены, или в алфавитном порядке по именам, или может группировать некоторые переменные вместе, если это приводит к более быстрому коду.
Если вам нужно, чтобы переменные располагались в определенном порядке, вы можете обернуть их в массив или структуру.
(Если бы вы посмотрели на сгенерированный машинный код, то, скорее всего, обнаружили бы, что переменные не «заталкиваются в стек» одна за другой. Вместо этого компилятор, вероятно, сгенерирует одну инструкцию для корректировки указателя стека на определенное количество байтов, эффективно выделяя один фрагмент памяти для хранения всех локальных переменных для функции или блока. Код, обращающийся к данной переменной, затем будет использовать ее смещение в кадре стека.)
А поскольку ваша функция ничего не делает со своими локальными переменными, компилятор может вообще не выделять для них место, особенно если вы запрашиваете оптимизацию с помощью
-O3
или чего-то подобного.Компилятор может расположить локальные переменные так, как ему хочется. Он может даже не выделять их вообще (например, если они не используются или оптимизированы путем распространения/цисцизации/хранения в реестре/и т. д.), или выделить одно и то же место в стеке для нескольких локальных объектов, которые не пересекаются в реальном времени. диапазоны.
Нет общих деталей реализации, чтобы описать, как это делает конкретный компилятор, поскольку это может измениться в любое время.
Как правило, компиляторы пытаются сгруппировать переменные одинакового размера (и/или выравнивания) вместе, чтобы свести к минимуму неиспользуемое пространство из-за «пробелов», но существует так много других факторов.
структуры и массивы имеют немного разные требования, но я считаю, что это выходит за рамки этого вопроса.