rzutowanie const_cast

0

Hej
Próbuję utrwalić sobie wiedzę z rzutowania i zdziwiłem się jak mi w programie taki oto output wyskoczył:

#include <stdio.h>
#include <iostream>
using namespace std;

int main() {
   const int a = 20;
   int* c = const_cast<int *>(&a);
   *c = 40;
   cout << "new value is " << *c << endl;
   cout << "new value is " << a << endl;
      
   cout << "addr of a = " << &a << endl;
   cout << "addr of a = " << c << endl;
   
   return 0;
}

Output:

new value is 40
new value is 20
addr of a = 0x7ffccdb8c904
addr of a = 0x7ffccdb8c904

Próbuję sobie to jakoś wytłumaczyć i strzelam, że w komórce pamięci 0x7ffccdb8c904 jest wartość tak naprawdę nowa czyli 20. Natomiast dla zmiennej 'a' kompilator wypisał mi 40 zamiast 20 bo już w momencie kompilacji jest ustawione 20. Czy tak można wyjaśnić to zjawisko? A może jednak mamy tu do czynienia z undefined behaviorem i to że mi akurat powiodło się w kompilatorze wandbox czy onlinegdb to nic nie znaczy. Ogólnie spodziewałem się podczas kompilacji jakeigoś warninga a program, że się scrashuje.

2

Robiąc tego const_cast doprowadzasz do undefined behavior.
Zależnie od kompilatora i jego ustawień, wynik może być różny w tym crash.

W twoim przypadku kompilator umieścił wartość stałą w przestrzeni adresowej niechronionej.
Równocześnie podczas użycia a wstawił stałą znaną podczas kompilacji prosto do wywołania operatora << zamiast odnosić się do instancji tej stałej.
Dobrze to widać tutaj: https://www.godbolt.org/z/N4QtBh
Interesuje cię te fragmenty:

        mov     DWORD PTR [rbp-12], 20 // stała jest umieszczona na stosie
....
        // wypisanie *c
        mov     rax, QWORD PTR [rbp-8] // odczytaj wskaźnik
        mov     eax, DWORD PTR [rax] // odczytaj wartość wskazywaną
        mov     esi, eax // przekaż wartość z pod wskaźnika
        mov     rdi, rdx
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
....
        mov     esi, 20 // nie ma odczytu "a" tylko wpisane jest bezpośrednio 20
        mov     rdi, rax
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)

Mała modyfikacja i jest crash: https://www.godbolt.org/z/W8DABn

0

const_cast używasz tylko w przypadku, gdy chcesz się pozbyć modyfikatora const. Nie zmienia to faktu, że ten const dalej obowiązuje i nie możesz zrobić nic, co by nie przeszło z constem. Typowy przypadek użycia to przykładowo integracja z zewnętrznymi bibliotekami. Stare wersje OpenSSLa nie używały constów, choć było wiadomo, że pewne argumenty nie były modyfikowane. W takim przypadku const_cast pozwala na używanie constów po stronie c++

0
MarekR22 napisał(a):

Robiąc tego const_cast doprowadzasz do undefined behavior.

Nie robiąc const_cast, tylko próbując zmienić wartość tak skastowanego inta.

const int a = 20;
int* c = const_cast<int *>(&a);
*c = 40; // UB

1 użytkowników online, w tym zalogowanych: 0, gości: 1