Порядок выделения локальной переменной на стеке

Используйте пространственные расширения MySQL с GIS.

26
задан David 9 July 2009 в 05:55
поделиться

8 ответов

Интересно, что если вы добавите дополнительный int * ret2 в function1, тогда в моей системе порядок будет правильным, тогда как он не работает только для 3 локальных переменных. Я предполагаю, что это упорядочено таким образом, чтобы отразить стратегию распределения регистров, которая будет использоваться. Либо так, либо произвольно.

0
ответ дан 28 November 2019 в 07:25
поделиться

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

void function1() {
    struct {
        int x;
        int y;
        int z;
        int *ret;
    } locals;
}

Если мне не изменяет память, спецификация гарантирует, что & ret> & z> & y> & x . Я оставил свой K&R на работе, поэтому я не могу цитировать главы и стихи.

17
ответ дан 28 November 2019 в 07:25
поделиться

Обычно это связано с проблемами выравнивания.

Большинство процессоров медленнее выбирают данные, не выровненные по словам процессора. Им приходится собирать его на части и соединять вместе.

Вероятно, происходит то, что они складывают вместе все объекты, которые больше или равны оптимальному выравниванию процессора, а затем плотнее упаковывают вещи, которые могут не быть выровнены . Так уж получилось, что в вашем примере все массивы char имеют размер 4 байта, но я уверен, что если вы сделаете их 3 байтами, они все равно окажутся в тех же местах.

Но если вы имел четыре однобайтовых массива, они могут оказаться в одном 4-байтовом диапазоне или выровнены в четыре отдельных.

Все дело в том, что проще всего (что означает «самый быстрый») для процессора.

5
ответ дан 28 November 2019 в 07:25
поделиться

ISO C не только ничего не говорит о порядке локальных переменных в стеке, но даже не гарантирует, что стек вообще существует. Стандарт просто говорит об объеме и времени жизни переменных внутри блока.

8
ответ дан 28 November 2019 в 07:25
поделиться

Я предполагаю, что это как-то связано с тем, как данные загружаются в регистры. Возможно, с массивами char компилятор творит чудеса, чтобы делать что-то параллельно, и это как-то связано с позицией в памяти, чтобы легко загружать данные в регистры. Попробуйте скомпилировать с разными уровнями оптимизации и попробуйте вместо этого использовать int buffer1 [1] .

0
ответ дан 28 November 2019 в 07:25
поделиться

Это также может быть проблемой безопасности?

int main()
{
    int array[10];
    int i;
    for (i = 0; i <= 10; ++i)
    {
        array[i] = 0;
    }
}

Если массив ниже в стеке, чем i, этот код будет бесконечно зацикливаться (потому что он ошибочно обращается к массиву [10] и обнуляет его, что является я). При размещении массива выше в стеке попытки доступа к памяти за пределами стека с большей вероятностью будут касаться нераспределенной памяти и сбоя, а не вызывать неопределенное поведение.

Я экспериментировал с тем же кодом один раз с gcc, и не смог заставить его выйти из строя, за исключением определенной комбинации флагов, которую я сейчас не помню .. В любом случае, он поместил массив на несколько байтов от i.

0
ответ дан 28 November 2019 в 07:25
поделиться

Это полностью зависит от компилятора. Помимо этого, некоторые процедурные переменные могут вообще никогда не помещаться в стек, поскольку они могут проводить всю свою жизнь в регистре.

0
ответ дан 28 November 2019 в 07:25
поделиться

Итак, я еще немного поэкспериментировал и вот что нашел. Кажется, это основано на том, является ли каждая переменная массивом. Учитывая этот ввод:

void f5() {
        int w;
        int x[1];
        int *ret;
        int y;
        int z[1];
}

Я получаю это в gdb:

(gdb) p &w
$1 = (int *) 0xbffff4c4
(gdb) p &x
$2 = (int (*)[1]) 0xbffff4c0
(gdb) p &ret 
$3 = (int **) 0xbffff4c8
(gdb) p &y
$4 = (int *) 0xbffff4cc
(gdb) p &z
$5 = (int (*)[1]) 0xbffff4bc

В этом случае сначала обрабатываются int s и указатели, последний объявляется на вершине стека и сначала объявляется ближе к дно. Затем массивы обрабатываются в направлении, противоположном тому, чем раньше объявление, тем выше в стеке. Я уверен, что для этого есть веская причина. Интересно, что это такое.

9
ответ дан 28 November 2019 в 07:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: