crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

 
Ответить в данную темуНачать новую тему
> Когда применять const_cast
AXELman4ever
  опции профиля:
сообщение 24.9.2011, 2:20
Сообщение #1


Студент
*

Группа: Участник
Сообщений: 31
Регистрация: 22.9.2011
Пользователь №: 2902

Спасибо сказали: 0 раз(а)




Репутация:   0  


Всем добрый!

Пытаюсь понять принцип работы const_cast. Для интереса про-инициализировал значение (int) переменной в стеке, вместо того, чтобы создать на него указатель. После чего, загнал ссылку переменной и решил привести к типу pointer на int, дабы снять с переменной константность:

const int i = 5;
int *ptr = const_cast <int*> (&i);


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

И вот чему я был удивлен. Адрес, который хранит указатель и адрес ссылки - одинаковы, но значения у них разные:

qDebug() << "*ptr: "<< ptr << "-" << *ptr; // *ptr: 0x28ff10 - 6
qDebug() << "&i: "<< &i << "-" << i; // &i : 0x28ff10 - 5

Объясните пожалуйста, как так происходит? и почему const_cast нельзя вызвать для константных типов значений?

PS: Посоветуйте какую-нибудь легкодоступную для понимая книжечку по азам С++, если можно. Книги читать не люблю, но, так понимаю, без них никак. Заранее благодарен.

Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Алексей1153
  опции профиля:
сообщение 24.9.2011, 9:58
Сообщение #2


фрилансер
******

Группа: Участник
Сообщений: 2941
Регистрация: 19.6.2010
Из: Обливион
Пользователь №: 1822

Спасибо сказали: 215 раз(а)




Репутация:   34  


мдя, что курим ? :)

const int i = 5;
int *ptr = (int*)&i;

(*ptr)++; //инкремент i


или

const int i = 5;
((int&)i)++;//инкремент i


но лучше так не делать. Константа должна оставаться константой

ещё лучше применть енум

enum{ i = 5};


тогда таких вопросов точно не возникнет

Сообщение отредактировал Алексей1153 - 24.9.2011, 10:03
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
BRE
  опции профиля:
сообщение 24.9.2011, 10:35
Сообщение #3


Профессионал
*****

Группа: Участник
Сообщений: 1112
Регистрация: 6.3.2009
Из: Ростов-на-Дону
Пользователь №: 591

Спасибо сказали: 264 раз(а)




Репутация:   44  


Цитата(Алексей1153 @ 24.9.2011, 10:58) *
но лучше так не делать. Константа должна оставаться константой

Это точно. Так же как и пользоваться сишным приведением в C++. ;)
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Влад
  опции профиля:
сообщение 24.9.2011, 11:29
Сообщение #4


Участник
**

Группа: Участник
Сообщений: 146
Регистрация: 20.3.2009
Из: Санкт-Петербург
Пользователь №: 627

Спасибо сказали: 46 раз(а)




Репутация:   8  


Компилятор GCC 4.5
#include <iostream>
using namespace std;

const int i = 5;

int main()
{
    int *ptr = (int*)&i;
    (*ptr)++;   // ожидается инкремент i
                // Хе-хе. Как бы не так! Здесь реально происходит Access Violation

    return 0;
}
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Гость_D_*
сообщение 24.9.2011, 12:36
Сообщение #5





Гости








    


Цитата(Влад @ 24.9.2011, 11:29) *
Компилятор GCC 4.5
#include <iostream>
using namespace std;

const int i = 5;

int main()
{
    int *ptr = (int*)&i;
    (*ptr)++;   // ожидается инкремент i
                // Хе-хе. Как бы не так! Здесь реально происходит Access Violation

    return 0;
}


Здесь происходит Access Violation по той причине, что константа у вас находится в статической области памяти. В этом случае измнять ее не получится, поскольку она помещается в специального рода память.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Гость_D_*
сообщение 24.9.2011, 12:41
Сообщение #6





Гости








    


Вот вам ответ, почему так происходит

Код:
int main(int argc, char* argv[])
{
        const int i = 5;
        int *p = const_cast<int *>(&i);

        ++(*p);

        printf("&i: %d i:  %d\n", &i, i);
        printf("p:  %d *p: %d\n", p, *p);
        getchar();

        return 0;
}


А вот что мы увидем если компилировать его в ассемблерный код. Это только фрагмены:

            const int i = 5;                      ; валяется в стеке со смещением ebp-4
;
    ?debug L 5                                  ; ерунда какая-то
@1:
    mov       dword ptr [ebp-4],5      ; инициализируем i
;
;            int *p = (int *)(&i);                ; валяется в стеке со смещением ebp-8
;
    ?debug L 6
    lea       eax,dword ptr [ebp-4]    ; запишем в eax эффективный адрес двойного слова по адресу, записанному в стеке со
                                                                    ; смещением ebp-4
    mov       dword ptr [ebp-8],eax  ; инициализируем этим указатель p, который валяется в стеке со смещением ebp-8
;
;
;            ++(*p);                                  ; момент истины
;
    ?debug L 8                                  ; какая-то фигня
    mov       edx, dword ptr [ebp-8] ; поместим в edx то, что записано в стеке со смещением ebp-8, то есть p, в котором хранится
                                                                    ; адрес i
    inc       dword ptr [edx]                ; теперь взять и инкременировать то, что лежит по адресу записаному в edx. А в edx у нас
                                                                    ; валяется значение p инициализированное эффективным адресом того, что записаным в
                                                                    ; стеке ebp-4, которое и есть цифра 5.
                                                                    ; то есть теперь у нас *p == 6 и, следовательно, i == 6;

;            printf("&i: %d i:  %d\n", &i, i);
;
    ?debug L 10                                   ; это не интересно

    push      5                                        ; вот здесь ответ, сразу значение 5 вплевывает в стек (!!!!!!!!!!!!!!!!!!!!!!!!!!)
    lea       ecx,dword ptr [ebp-4]       ;  поместить в ecx эффективный адрес двойного слова лежащего в стеке со смещением
                                                                       ; [ ebp-4], то есть наш &i
    push      ecx                                    ; плюнуть это в стек
    push      offset s@                         ; строка форматирования для вызова printf, вплевываем адрес ее смещения в сегменте
                                                                       ; данных
    call      @_printf                              ; печатаем
    add       esp,12                               ; чистим стек
;
;            printf("p:  %d *p: %d\n", p, *p);
;
    ?debug L 11                                  ; далее выводится уже не константное значение, а значение того, что лежит по адресу *p
    mov       eax,dword ptr [ebp-8]    
    push      dword ptr [eax]
    push      dword ptr [ebp-8]
    push      offset s@+15
    call      @_printf
    add       esp,12


По русски это означает, что срабатывает оптимизация зависящая от реализации конкретного компилятора. Поскольку на этапе компиляции, здесь значение const i уже известно, компилятор при выводе его значения сразу подставляет число 5. При этом в ассемблерном коде видно, что при инкременте значение i действительно изменяется.
У Страуструпа написано, что подобного рода приведения создают ситуации в результате которых поведение константных объектов становится неопределенным

А еще, например, можно сделать так:
int main(int argc, char* argv[])
{
        const int *i = new int;
        int *p = const_cast<int *>(i);

        *p = 5;

        printf("i:  %d *i:  %d\n", i, *i);
        printf("p:  %d *p: %d\n", p, *p);
        getchar();

        delete i;

        return 0;
}

Тогда все будет уорректно. Поскольку значение константы на этапе компиляции здесь не известно.

Лёгких книжек по С++ не ищите. В плане понимания... мне очень нравится Философия С++ Брюса Эккеля. Совсем для начала можно Липпмана... у Эккеля в начале много абстрактного и исторического, поэтому может быть скучно. Но он хорош, он оперирует к размышлениям о языке, о его конструкциях и концепциях... кроме того у него большой опыт в преподавании С++. Со Страуструпа сразу начинать я бы не советовал, но прочесть тоже нужно.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Iron Bug
  опции профиля:
сообщение 24.9.2011, 14:55
Сообщение #7


Профессионал
*****

Группа: Модератор
Сообщений: 1611
Регистрация: 6.2.2009
Из: Yekaterinburg
Пользователь №: 533

Спасибо сказали: 219 раз(а)




Репутация:   12  


Цитата(Гость_D_* @ 24.9.2011, 15:36) *
она помещается в специального рода память

на самом деле, она помещается в сегмент кода. а попытки изменения в этом сегменте всегда вызывают access violation.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Алексей1153
  опции профиля:
сообщение 24.9.2011, 16:01
Сообщение #8


фрилансер
******

Группа: Участник
Сообщений: 2941
Регистрация: 19.6.2010
Из: Обливион
Пользователь №: 1822

Спасибо сказали: 215 раз(а)




Репутация:   34  


Цитата
Так же как и пользоваться сишным приведением в C++.


я не суеверный


Цитата
// Хе-хе. Как бы не так! Здесь реально происходит Access Violation


что вполне ожидаемо, особенно в релизе. Ибо не трогай константу :)

Сообщение отредактировал Алексей1153 - 24.9.2011, 15:59
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
AXELman4ever
  опции профиля:
сообщение 24.9.2011, 17:52
Сообщение #9


Студент
*

Группа: Участник
Сообщений: 31
Регистрация: 22.9.2011
Пользователь №: 2902

Спасибо сказали: 0 раз(а)




Репутация:   0  


В очередной раз благодарю всех отписавшихся. За 1 день обитания на форуме узнаю больше чем за 1 год обитания в ВУЗе :)
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 30.11.2024, 8:09