2013-12-22 4 views
1

Я читаю книгу на C++ и имею проблему со статическим литьем. Вот функция:Тип Casting in C++

void fun(int*pi) 
{ 
    void *pv = pi 
    int *pi2 = static_cast<int*>(pv); //explicit conversion back to int* 
    double *pd3 = static_cast<double*>(pv); //unsafe 

} 

Последнее утверждение:

double*pd3 = static_cast<double*>(pv); 

считается небезопасным. Я не понимаю, почему это считается небезопасным.

+2

Поскольку вы сообщаете компилятору, что существует объект 'double', на который указывает' pv', тогда как на самом деле его нет. –

+0

Поскольку хранилище, используемое для int, значительно меньше, чем у двойника. Вы можете в конечном итоге либо использовать неправильные данные, либо даже перезаписать переменную, чтобы следовать/предшествует тому, на что указывает pi. – cup

+0

Подсказка: 'int' и' double' - это не одно и то же, и они не обязательно занимают одно и то же пространство. – Shoe

ответ

6

Актерский переосмысливает биты заостренный к int, плюс, возможно, биты некоторой следующей памяти (если есть!), Как значение double.

A double (1) обычно больше, чем int, и (2) имеет внутреннюю структуру.

Точка (1) означает, что любое использование указателя результата разыменования может иметь доступ к памяти, которая просто недоступна, за пределами int.

Point (2) означает, что произвольная bitpattern, может быть недействительным как double bitpattern, и может привести к аппаратное исключение в а.к.а. «ловушку», когда он используется. С точки зрения C++ это неопределенное поведение. С точки зрения практического программирования это, как правило, «сбой».

В отличии от доступа к битам double как int, как правило, в-практика безопасно, даже если это формально УБ, потому что (1) int обычно меньше или равен по размеру double, и (2) int обычно не имеет никаких недопустимых битовых шаблонов. Однако, в зависимости от параметров компилятора, компилятор может не быть счастлив сделать это напрямую.


Выше я забыл упомянуть выравнивание как Локи Astari отметил в комментарии. И это причина (3) для небезопасности. В качестве примера, с некоторой данной реализацией, int может быть разрешен иметь адрес, который является кратным 4, тогда как double может потребоваться проживать по адресу, который является кратным 8. Затем разыменованный указатель может получить доступ к double по адресу, который не является кратным 8, вызывая ловушку (более формально, UB, где что-то может случиться).

+0

Обратите внимание, что «на практике безопасно» неверно, учитывая, что современные компиляторы оптимизируют агрессивно на основе любого типа UB. –

+0

Также стоит упомянуть выравнивание и доступ. –

+0

+1 Хорошее объяснение – Freedom911

2

Поскольку указатель double отличается от указателя int, и если вы попытаетесь его использовать, вы можете получить ошибку сегментации. Они не обязательно совместимы.


Вы можете попробовать лить значение, указанное pi2.

void fun(int*pi) 
{ 
    void *pv = pi; 
    int *pi2 = static_cast<int*>(pv); 
    double d = static_cast<double>(*pi2); 
    std::cout << d; // 42 
} 

int main() 
{ 
    int i = 42; 
    fun(&i); 
}