Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Размещение переменных в стеке


Автор: FiMa1 10.5.2007, 16:07
Пытаюсь проанализировать размещение переменных в стеке... Почему разница между адресами переменных a и b равна 12 ?
Код
#include <iostream>
using namespace std;

void func( int p1, int& p2 )
{
    int a, b=3;

    cout << "size of int: " << '\t' << sizeof(int) << endl;
    cout << "size of char: " << '\t' << sizeof(char) << endl;

    cout << "p1 address:" << "\t" << &p1 << " value is " << p1 << endl;
    cout << "p2 address " << "\t" << &p2 << " value is " << p2 << endl;

    cout << "a address:" << "\t" << &a << " not initialized" << endl;
    cout << "b address:" << "\t" << &b << " value is " << b << endl;
}

int main( void )
{
    int n1 = 1;
    int n2 = 2;
    
    func( n1, n2 );
    
    return 0;
}

Результат работы программы:
size of int:     4
size of char:  1
p1 address:   0012FE7C value is 1
p2 address:   0012FF54 value is 2
a address:     0012FE6C not initialized
b address:     0012FE60 value is 3

Автор: Sartorius 10.5.2007, 16:14
 почитай disassembly  и все увидишь...

Автор: Daevaorn 10.5.2007, 16:17
Цитата(FiMa1 @  10.5.2007,  17:07 Найти цитируемый пост)
Почему разница между адресами переменных a и b равна 12 ?

А почему бы и нет? Компилятор как хочет, так переменные и располагает.

Добавлено через 7 минут и 49 секунд
А вообще выключи ещё отладочную информациюsmile

Автор: FiMa1 10.5.2007, 16:36
Цитата
А почему бы и нет? Компилятор как хочет, так переменные и располагает.

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

Автор: Daevaorn 10.5.2007, 17:00
Цитата(FiMa1 @  10.5.2007,  17:36 Найти цитируемый пост)
Видимо я неправильно понимаю организацию работы стека... Я предполагал, что объекты ложатся в стек последовательно

то что последовательно это факт. по другому никак. другое дело, что между ними может быть ещё что-то, например выравнивание.

Автор: FiMa1 10.5.2007, 17:30
А можно чуть подробнее про это выравнивание? Первый раз слышу... Что-то связанное с отладочной информацией?

Автор: Daevaorn 10.5.2007, 17:41
Цитата(FiMa1 @  10.5.2007,  18:30 Найти цитируемый пост)
А можно чуть подробнее про это выравнивание? Первый раз слышу... 

http://en.wikipedia.org/wiki/Data_structure_alignment
Цитата(FiMa1 @  10.5.2007,  18:30 Найти цитируемый пост)
 Что-то связанное с отладочной информацией

ну эти вещи не связаны. просто в твоем случае из-за режима отладки такие результаты

Автор: FiMa1 10.5.2007, 17:47
Всем спасибо за участие!

Автор: FiMa1 11.5.2007, 17:24
Выключил отладочную информацию - выставил Release в VS 2005.
Код
#include <iostream>
using namespace std;

int main(void)
{
    int n1 = 1;
    int n2 = 2;
    
    cout << sizeof(int) << endl;
    cout << &n1 << '-' << &n2 << '=' << &n1 - &n2 << endl;
    
    return 0;
}

Результат работы программы:
4
0012FEE4-0012FEE0=1

Почему 0012FEE4-0012FEE0=1, а не 4. Если все-таки 4, то почему 4 байта, ведь имеем две целых переменных по 4 байта каждая, т.е. 8 байт.

Автор: nickless 11.5.2007, 17:35
Цитата(FiMa1 @  11.5.2007,  16:24 Найти цитируемый пост)
&n1 - &n2

Для типизированных указателей разница (да и всё остальное тоже) вычисляется в размерах типов, т.е. в этом случае в intах.

Цитата(FiMa1 @  11.5.2007,  16:24 Найти цитируемый пост)
Если все-таки 4, то почему 4 байта, ведь имеем две целых переменных по 4 байта каждая, т.е. 8 байт

Если сделать 
Код

reinterpret_cast<char*>(&n1) - reinterpret_cast<char*>(&n2)

то выдаст 4, потому что в указателе адрес _начала_ переменной

Автор: FiMa1 14.5.2007, 14:54
Цитата(nickless @ 11.5.2007,  17:35)
то выдаст 4, потому что в указателе адрес _начала_ переменной

Имеем нижеприведенный код.
Код
#include <iostream>
using namespace std;
void STACK(int p1, int& p2) 
{ 
    cout << "STACK 1::p1 "  << "\t" << &p1  << " value is " << p1 << endl;
    cout << "STACK 1::p2 "  << "\t" << &p2  << " value is " << p2 << endl;

    int i=1, i1=3;
    cout << "STACK 1::i "  << "\t" << &i  << " value is " << i << endl;
    cout << "STACK 1::i1 " << "\t" << &i1 << " value is " << i1 << endl;
    { 
        int i=2, i2=5;
        cout << "STACK 1.1::i "  << "\t" << &i  << " value is " << i << endl;
        cout << "STACK 1.1::i2 " << "\t" << &i2 << " value is " << i2 << endl;
        {
            int i=9, i3=7;
            cout << "STACK 1.1.1::i " << "\t" << &i << " value is " << i << endl;
            cout << "STACK 1.1.1::i3 " << &i3 << " value is " << i3 << endl;
        }
        i = 8;
        cout << "STACK 1.1::i " << "\t" << &i << " value is " << i << endl;
        {
            double d = (double)2 * i;
            cout << "STACK 1.1.1::d " << "\t" << &d << " value is " << d << endl;
        }
        i = i1;
        cout << "STACK 1.1::i " << "\t" << &i << " value is " << i << endl;
    }
    {
        char s[] = "abs";
        cout << "STACK 1.1::s " << "\t" << &s << " value is " << s << endl;
    } 
}

int main(void)
{
    int i1 = 1, i2 = 2;

    cout << &i1 << ' ' << &i2 << endl;

    STACK(i1, i2);

    return 0;
}

Результат работы программы:
0012FEE4 0012FEE0
STACK 1::p1     0012FED4 value is 1
STACK 1::p2     0012FEE0 value is 2
STACK 1::i      0012FEC0 value is 1
STACK 1::i1     0012FEB8 value is 3
STACK 1.1::i    0012FEB4 value is 2
STACK 1.1::i2   0012FEC4  value is 5
STACK 1.1.1::i  0012FEC8 value is 9
STACK 1.1.1::i3 0012FEBC value is 7
STACK 1.1::i    0012FEB4 value is 8
STACK 1.1.1::d  0012FEC8 value is 16
STACK 1.1::i    0012FEB4 value is 3
STACK 1.1::s    0012FEBC value is abs

Почему 0012FEC0 (STACK 1::i) - 0012FEB8 (STACK 1::i1) = 8 (отладочная информация выключена - Release ) ? Да и вообще, по-моему, на моей машине стек должен "расти" вниз...

Автор: JackYF 14.5.2007, 16:13
Цитата(FiMa1 @  14.5.2007,  14:54 Найти цитируемый пост)
Почему 0012FEC0 (STACK 1::i) - 0012FEB8 (STACK 1::i1) = 8


Скорее всего, имеем, опять-таки, выравнивание.

Автор: FiMa1 14.5.2007, 16:20
Цитата(JackYF @ 14.5.2007,  16:13)
Скорее всего, имеем, опять-таки, выравнивание.

От этого можно хоть как-нибудь избавиться? Хочу просмотреть чисто работу стека... IDE VS 2005.

Автор: Daevaorn 14.5.2007, 16:50
Цитата(FiMa1 @  14.5.2007,  17:20 Найти цитируемый пост)
Хочу просмотреть чисто работу стека... IDE VS 2005.

А чем эта не чиста? Теорию можно прочитать в умных книжках, а тут практика - тоже полезно.

Автор: FiMa1 15.5.2007, 11:38
Цитата(Daevaorn @ 14.5.2007,  16:50)
А чем это не чисто? Теорию можно прочитать в умных книжках, а тут практика - тоже полезно.
  • Насколько я понял, согласно теории, на моей машине стек должен "расти" вниз, мы же имеем, что переменная i в блоке STACK 1 расположилась начиная с адреса 0012FEC0, идущая после нее переменная i1 (все тот же блок STACK 1) располагается, судя по выведенным результатам, по адресу 0012FEB8. Не получаем, ситуации что стек "растет" вниз....
  • Ок, пусть стек растет вверх, а не вниз, но разница между адресами 0012FEC0 - 0012FEB8 составляет 8 байт. между тем мы уже выяснили, что должно "4, потому что в указателе адрес _начала_ переменной" (см. выше)...
  • Если таки стек растет вверх, то почему в блоке STACK 1.1, к примеру, переменная i начинается с 0012FEB4, а i2 - с 0012FEC4. Стек теперь растет вниз?.. Ну а разница 0012FEC4 - 0012FEB4, не поверите, составляет 10...

Автор: v_nikolaev 15.5.2007, 13:03
Чего гадать? Не лучше ли выложить дизассемблер этой функции сюда, а потом и говорить? Заодно сказать, что за компилятор.

Может, там вообще в стек ничего не записывается, а печатаются значения начала стека процедуры со статически известным сдвигом  smile 

Автор: FiMa1 15.5.2007, 13:47
Цитата(v_nikolaev @ 15.5.2007,  13:03)
Чего гадать? Не лучше ли выложить дизассемблер этой функции сюда, а потом и говорить? Заодно сказать, что за компилятор.

Про компилятор говорилось - VS 2005. Дизасм прилагается...

Автор: adLucem 15.5.2007, 14:05
Удалите из вывода ссылки на строковые переменные (потому что они помещаются в стек для передачи в функции) и наслаждайтесь. 

Код

#include <iostream>
using namespace std;

#pragma push ( 1 )

void STACK(int p1, int& p2) 
{ 
    cout << &p1  <<  endl;
    cout << &p2  <<  endl;
    int i=1;
    int i1=3;
    cout << &i << endl;
    cout << &i1 << endl;
    { 
        int i=2, i2=5;
        cout << &i  << endl;
        cout << &i2 << endl;
        {
            int i=9, i3=7;
            cout << &i << endl;
            cout << &i3 << endl;
        }
        i = 8;
        cout << &i << endl;
        {
            double d = (double)2 * i;
            cout << &d<< endl;
        }
        i = i1;
        cout << &i << endl;
    }
    {
        char s[] = "abs";
        cout << &s << endl;
    } 
}
int main(void)
{
    int i1 = 1, i2 = 2;
    cout << &i1 << ' ' << &i2 << endl;
    STACK(i1, i2);
    return 0;
}


Вывод
0012FF70 0012FF6C
0012FF60
0012FF6C
0012FF48
0012FF44
0012FF40
0012FF4C
0012FF50
0012FF54
0012FF40
0012FF54
0012FF40
0012FF54

Автор: FiMa1 15.5.2007, 16:05
Спасибо!

Автор: FiMa1 15.5.2007, 16:52
Цитата(adLucem @ 15.5.2007,  14:05)
Удалите из вывода ссылки на строковые переменные (потому что они помещаются в стек для передачи в функции) и наслаждайтесь.

Ммм.. не понял чем наслаждаться... Скомпилировал один в один, у меня вот какой результат:
0012FEE4 0012FEE0
0012FED4
0012FEE0
0012FEBC
0012FEB8
0012FEB4
0012FEC0
0012FEC4
0012FEC8
0012FEB4
0012FEC8
0012FEB4
0012FEC8

Т.о. 0012FEBC - i, 0012FEB8 - i1. Стек "растет вверх"... Дальше 0012FEB4 - i, 0012FEC0 - i2, стек растет "вниз". Да и у вас, я смотрю тоже i и i1 разместились с 0012FF48 и
0012FF44, соответственно...?
Что такое #pragma push ( 1 ), прокомментируйте тоже, пожалуйста.

Автор: JackYF 15.5.2007, 17:15
Цитата(FiMa1 @  15.5.2007,  11:38 Найти цитируемый пост)
к, пусть стек растет вверх, а не вниз, но разница между адресами 0012FEC0 - 0012FEB8 составляет 8 байт. между тем мы уже выяснили, что должно "4, потому что в указателе адрес _начала_ переменной" (см. выше)...


Выравнивание. Как часть явной или неявной оптимизации компилятора. Или лучше пускай переменные будут вплотную друг к дружке, но код твой выполняться медленнее?

Цитата(FiMa1 @  15.5.2007,  11:38 Найти цитируемый пост)
Если таки стек растет вверх, то почему в блоке STACK 1.1, к примеру, переменная i начинается с 0012FEB4, а i2 - с 0012FEC4. Стек теперь растет вниз?.. Ну а разница 0012FEC4 - 0012FEB4, не поверите, составляет 10...


16.

Цитата(FiMa1 @  15.5.2007,  11:38 Найти цитируемый пост)
Насколько я понял, согласно теории, на моей машине стек должен "расти" вниз, мы же имеем, что переменная i в блоке STACK 1 расположилась начиная с адреса 0012FEC0, идущая после нее переменная i1 (все тот же блок STACK 1) располагается, судя по выведенным результатам, по адресу 0012FEB8. Не получаем, ситуации что стек "растет" вниз....


Скорее всего, опять выравнивание.

P.S. Ну какая тебе нафиг разница, в каком порядке у тебя переменные в стеке лежат? Как программисту на С/С++?
Пиши тогда на Ассемблере, сам всем будешь управлять smile

Автор: FiMa1 15.5.2007, 17:33
Ну на том и порешили. Всем большое спасибо! Топик закрыт, ушел учить ассемблер......

Автор: v_nikolaev 24.5.2007, 10:19
Цитата(FiMa1 @ 15.5.2007,  16:52)

Что такое #pragma push ( 1 ), прокомментируйте тоже, пожалуйста.


насколько я помню, это работа с выравниванием, только чтобы вырубить его, кажется, нужно писать 
#pragma push ( 0 )  - или я что-то путаю...

2 JackYF:

программисту C/C++ и вправду не к чему это знать, а иногда даже вредно это знать smile , как говорят иногда smile

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

Автор: Fazil6 24.5.2007, 10:34
Цитата(FiMa1 @  15.5.2007,  16:52 Найти цитируемый пост)
Что такое #pragma push ( 1 ), прокомментируйте тоже, пожалуйста.


вообще-то я так не видел никогда. Обычно
Код

// установить выравнивание по байту
#pragma pack( push, 1 ) 


// вернуть старое
#pragma pack( pop )


так еще можно
Код

#pragma pack ( 1 )

Автор: JackYF 24.5.2007, 13:06
Цитата(v_nikolaev @  24.5.2007,  10:19 Найти цитируемый пост)
однако бывают случаи, когда умение правильно выравнивать важно - например, при связки C - ASM.


согласен, пожалуй. Но тогда будет такой геморрой на свою голову...

Автор: dumb 24.5.2007, 14:23
Цитата(v_nikolaev @  24.5.2007,  10:19 Найти цитируемый пост)
насколько я помню, это работа с выравниванием <...> программисту C/C++ и вправду не к чему это знать, а иногда даже вредно это знать

выравнивание "всплывает" при любой передаче структуры "наружу" - по сети, в файл, итд. так что знать такие вещи необходимо, чтобы не было smile

Автор: JackYF 24.5.2007, 16:57
Цитата(dumb @  24.5.2007,  14:23 Найти цитируемый пост)
при любой передаче структуры


Выравнивание элементов внутри структуры и выравнивание переменных в стеке - это разные вещи.

Цитата(dumb @  24.5.2007,  14:23 Найти цитируемый пост)
так что знать такие вещи необходимо, чтобы не было smile 

Безусловно, про выравнивания структур знать надо. А вот особенности компилятора здесь идут нафиг.

Автор: dumb 24.5.2007, 18:22
Цитата(JackYF @  24.5.2007,  16:57 Найти цитируемый пост)
Выравнивание элементов внутри структуры и выравнивание переменных в стеке - это разные вещи.

а. это я выпал из темы... вы ж про стек...
в таком случае вообще непонятно, причем тут выравнивание.
понятно, что можно выравнивать элементы в стеке на какие-то границы, но на практике выравнивание всегда идет на 4(не трогаем 16ти и 64ех битные режимы). и, afaik, возможности управлять выравниванием в стеке просто нету.
прагму какую-то по ходу дела изобрели... smile

почему автор топика решил, что переменные в стеке должны располагаться в порядке их объявления в исходнике - зОгадкО. smile

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

Автор: v_nikolaev 25.5.2007, 08:50
Да, но разговор тут уже зашёл не только о стеке, и pragma, кажется, к стеку отношения не имела, хотя я сам уже плохо помню, что ето за прагма smile

Выравнивание в стеке нуэно при связке асм-с, выравнивание в структуре и подавно нужно smile

Но затачиваться под работу стека с локальными переменными нельзя - это приводит к кривым последствиям, потому лучше этой темой не заморачиваться smile

я тоже когда-то обижался на оптимизатор, за то что он инварианты из цикла выносит и тп, а вот ты добавь обращение к адресу через указатель, глядишь, он и не вынесет инвариантов smile

удивляться, что прога делает только то, что от неё просят не стоит. smile

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)