char * для удвоения и возврата к char * снова (64-битное приложение) - PullRequest
1 голос
/ 27 сентября 2011

Я пытаюсь преобразовать char * в удвоение и обратно в char * снова.следующий код работает нормально, если созданное вами приложение 32-разрядное, но не работает для 64-разрядного.Проблема возникает, когда вы пытаетесь преобразовать обратно в char * из int.например, если hello = 0x000000013fcf7888, то преобразованное значение равно 0x000000003fcf7888, только последние 32 бита являются правильными.

#include <iostream>
#include <stdlib.h>
#include <tchar.h>
using namespace std;


int _tmain(int argc, _TCHAR* argv[]){

    char* hello = "hello";
    unsigned int hello_to_int = (unsigned int)hello;
    double hello_to_double = (double)hello_to_int;

    cout<<hello<<endl;
    cout<<hello_to_int<<"\n"<<hello_to_double<<endl;

    unsigned int converted_int = (unsigned int)hello_to_double;
    char* converted = reinterpret_cast<char*>(converted_int);

    cout<<converted_int<<"\n"<<converted<<endl;

    getchar();
    return 0;
}

Ответы [ 6 ]

6 голосов
/ 27 сентября 2011

В 64-разрядных Windows указатели являются 64-разрядными, а int - 32-разрядными. Вот почему вы теряете данные в старших 32-битных во время приведения. Вместо int используйте long long для хранения промежуточного результата.

char* hello = "hello";
unsigned long long hello_to_int = (unsigned long long)hello;

Внести аналогичные изменения для обратного преобразования. Но это не гарантирует правильной работы преобразований, поскольку double может легко представлять весь 32-битный целочисленный диапазон без потери точности, но это не так для 64-битного целого.

Кроме того, это не сработает

unsigned int converted_int = (unsigned int)hello_to_double;

Это преобразование просто усекает любые цифры после десятичной точки в представлении с плавающей запятой. Проблема существует, даже если вы измените тип данных на unsigned long long. Вам нужно будет reinterpret_cast<unsigned long long>, чтобы это заработало.

Даже после всего этого вы можете столкнуться с проблемами в зависимости от значения указателя. Преобразование в double может привести к тому, что значение будет , сигнализирующее, например, NaN , в результате чего ваш код может вызвать исключение.

Простой ответ: если вы не попробуете это для развлечения, не делайте подобные преобразования.

2 голосов
/ 27 сентября 2011

Нельзя привести char* к int в 64-битной Windows, потому что int - это 32 бита, а char* - 64-битная, потому что это указатель. Поскольку double всегда составляет 64 бита, вы можете избежать приведения между double и char*.

1 голос
/ 27 сентября 2011

Пара проблем с кодированием любого целого числа (в частности, набора битов) в значение с плавающей запятой:

  1. Преобразования из 64-разрядных целых в двойные могут быть с потерями.Двойник имеет 53-бит фактической точности, поэтому целые числа выше 2^52 (дают или берут дополнительные 2) не обязательно будут представлены точно.
  2. Если вы решите переосмыслить битыуказателя в виде double вместо этого (через union или reinterpret_cast) у вас все еще будут проблемы, если вам случится кодировать указатель как набор битов, которые не являются допустимым двойным представлением.Если вы не можете гарантировать, что значение double никогда не будет записано обратно FPU, FPU может тихо преобразовать недопустимый тип double в другой недопустимый тип double (см. NaN ), т. Е. Значение типа double, представляющее то же самоезначение, но имеет разные биты. (См. Это для вопросов, связанных с использованием форматов с плавающей запятой в качестве битов.)

Вероятно, вы можете спокойно избежать необходимости кодировать 32-битный указатель в двойном виде, поскольку это будетопределенно вписывается в 53-битный диапазон точности.

1 голос
/ 27 сентября 2011

Если это работает в любой системе, где угодно, просто всем повезет и двигайтесь дальше. Преобразование указателя в целое число - это одно (до тех пор, пока целое число достаточно велико, вы можете обойтись без него), но двойное число - это число с плавающей запятой - то, что вы делаете, просто не имеет никакого смысла, потому что double не обязательно может представлять любое случайное число. Двойник имеет ограничения по дальности и точности, а также ограничения на то, как он представляет вещи. Он может представлять числа в широком диапазоне значений, но не может представлять КАЖДЫЕ числа в этом диапазоне.

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

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

Только не делай этого - он не должен работать.

0 голосов
/ 27 сентября 2011

Это, как и ожидалось.

Обычно char* будет 32 бит в 32-битной системе, 64 бит в 64-битной системе;double обычно составляет 64 бита в обеих системах.(Эти размеры типичны и, вероятно, правильны для Windows; язык допускает гораздо больше вариаций.)

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

Но вы конвертируете из указателя в целое число (которое разрешено, но определяется реализацией), а затем из целого числа в двойное число(что допустимо и имеет смысл для значащих числовых значений - но преобразованные значения указателя не имеют числового значения).Вы теряете информацию, потому что не все 64 бита двойного используются для представления величины числа;обычно для представления показателя используются 11 или около того битов.

То, что вы делаете, просто не имеет смысла.

Что именно вы пытаетесь достичь?Что бы это ни было, несомненно, есть лучший способ сделать это.

0 голосов
/ 27 сентября 2011

правильные только последние 32 бита.

Это потому, что int в вашей платформе имеет длину всего 32 бита.Обратите внимание, что reinterpret_cast гарантирует только то, что вы можете преобразовать указатель в int достаточного размера (не в вашем случае) и обратно.

...