2013-10-15 3 views
2

Как я могу наложить указатель на void на double, сохраняя точный двоичный файл, сохраненный в указателе void? Я думал, что это можно сделать с помощью reinterpret_cast<double>(voidp), но g ++ не позволяет мне. Я знаю, что вы можете наложить указатели void на целые числа, поэтому я попробовал reinterpret_cast<double>(reinterpret_cast<long>(voidp)), но, видимо, это тоже недействительно. sizeof(double) и sizeof(void*) оба 8, поэтому это не может быть вещь размера. Есть ли что-то, что я могу сделать для этого?Неверный листинг C++ из типа 'void *' для ввода 'double'

EDIT: двойник в этом случае не указывается указателем void, но/is/указатель void - сам указатель содержит данные, которые я хочу, он не указывает на нужные мне данные.

+0

Включите его в 'double *' – Kunal

+0

'double d = * ((double *) voidp)'? – clcto

+0

Почему вы хотите это сделать? В какой ситуации вы будете хранить 'double' в' void * '? –

ответ

10

Реинтерпретация прямой памяти, по определению, означает работу с lvalues. Самый простой подход должен был бы сделать это, хотя гипсе к ссылочного типа

double d = reinterpret_cast<double &>(voidp); 

Вы также можете сделать это с помощью указателя гипсе, как и другие ответы предложили, хотя он «перегружает» процедура с несколькими совершенно ненужными приложений оператора. Оба подхода эквивалентны, поскольку по определению reinterpret_cast ссылочному типу reinterpret_cast<T &>(v) эквивалентно версии указателя *reinterpret_cast<T *>(&v).

Однако вышеупомянутые подходы страдают от проблем с типом. Формально это делается просто незаконно. Вам не разрешено читать void * объектов как double объектов в C++.Прямая реинтерпретация памяти существует в C++ для повторной интерпретации объектов в виде массивов char s, а не для произвольного типа, как указано выше. Даже если мы проигнорируем формальную проблему и придерживаемся чисто «практических» соображений, пытаясь прямо переосмыслить значение void *, поскольку значение double может привести к совершенно неожиданным и бессмысленным результатам в компиляторе, который следует за строгой сглаживающей семантикой при выполнении оптимизаций.

Лучшая идея может быть memcpyvoid * объекта к объекту double

double d; 
assert(sizeof d == sizeof voidp); // <- a static assert would be even better 
memcpy(&d, &voidp, sizeof d); 

Кроме того, в C вы теперь разрешено использовать союзы для этой цели. Я не уверен, что официальное разрешение было сделано на C++, но оно, как правило, будет работать на практике.

+0

Yay для 'assert()' и 'memcpy()'. Что касается формального разрешения на C++: он по-прежнему явно запрещен (UB) для чтения члена неактивного профсоюза. –

+0

Я принял этот ответ вместо того, который я принял первым (Авиданборисов), потому что вы оба ответили на мой первоначальный вопрос, и предложение об использовании союза оказалось более полезным. – Monchoman45

+0

Метод объединения, в дополнение к обычному в результате UB, также может не генерировать лучший код: http://blog.regehr.org/archives/959. 'memcpy()', возможно, завернутый в функцию 'bit_cast <>()', должен быть предпочтительным методом для правильности, безопасности и удобства. @ Monchoman45 – bames53

2

Это вовсе не рекомендуется, но если у вас есть, использование:

*reinterpret_cast<double*>(&voidp) 
+1

Вы, вероятно, не захотите принять адрес пустоты *, хотя, возможно, это то, что он пытается сделать. Кто знает :) –

+0

Это то, что я искал - я думаю, я должен был уточнить, что указатель void/is/double, на самом деле он ничего не указывает. – Monchoman45

+0

@ Monchoman45: Зачем вам проходить 'double' в форме' void * '? Какое колдовство заставило вас пойти так низко? – LihO

-1
void* some_void_data = NULL; 
double* as_double = static_cast<double*>(some_void_data); 
double as_double_copy = *as_double; 
double as_double_copy_one_line = *static_cast<double*>(some_void_data); 

Очевидно, что вы хотели бы применить бросок к ненулевым указателем в режиме реального использования. static_cast может использоваться только для преобразования из одного типа указателя в другой тип указателя. Если вам нужна копия указателя, вы должны выполнить копию самостоятельно, разыменовав указатель, возвращаемый приложением.

This - хороший ресурс по литью.

4

Метод memcpy() должен быть вашим предпочтительным методом типа каламбуров:

double d = 100; 
void *x; 
std::memcpy(&x, &d, sizeof x); 

std::cout << x << '\n'; 

double d2; 
std::memcpy(&d2, &x, sizeof d2); 

std::cout << d2 << '\n'; 

Вы можете подумать, что это будет медленнее, чем гипс, но на самом деле компиляторы достаточно умны, чтобы признать, что происходит здесь и генерировать оптимальными код: http://blog.regehr.org/archives/959

Кроме того, этот метод не может привести к неопределенному поведению из-за нарушений псевдонимов, что может случиться с методами приведения или объединения.

Вы можете написать bit_cast оператор, чтобы сделать это более convienent и более безопасным:

http://pastebin.com/n4yDjBde

template <class Dest, class Source> 
inline Dest bit_cast(Source const &source) { 
    static_assert(sizeof(Dest)==sizeof(Source), "size of destination and source objects must be equal"); 
    static_assert(std::is_trivially_copyable<Dest>::value, "destination type must be trivially copyable."); 
    static_assert(std::is_trivially_copyable<Source>::value, "source type must be trivially copyable"); 

    Dest dest; 
    std::memcpy(&dest, &source, sizeof(dest)); 
    return dest; 
} 

Пример использования:

void *p = ...; 
double d = bit_cast<double>(p); 

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

+0

Зачем ему это делать? – LihO

+2

@LihO Я понятия не имею, но он явно попросил взять бит-шаблон «void *» и получить «double» с тем же битовым шаблоном. – bames53

+0

@LihO На самом деле я могу подумать об одном использовании: есть много API обратного вызова, которые позволяют вам передавать «void *» на обратный вызов. Если для обратного вызова требуется только double, и пользователь хочет избежать размещения удвоений в куче, а 'void *' окажется достаточно большим, то передача двойника напрямую, поскольку значение «void *» может иметь желаемые характеристики производительности. – bames53

Смежные вопросы