2013-04-03 5 views
5

У меня была небольшая игра с C++ сегодня и наткнулась на это, что я считал странным, но, возможно, более вероятно из-за недопонимания мной и отсутствия чистого C-кодирования в последнее время.C++. reinterpret_cast от double до unsigned char *

То, что я искал, первоначально было преобразование двойного в массив неподписанных символов. Я понял, что 64 бита double (sizeof (double) равно 8) теперь будут представлены в виде 8 8-битных символов. Для этого я использовал reinterpret_cast.

Итак, вот код для преобразования из double в char array, или, по крайней мере, я думал, что это то, что он делал. Проблема в том, что он возвращал 15 из strlen вместо 8, почему я не уверен.

double d = 0.3; 

unsigned char *c = reinterpret_cast<unsigned char*> (&d); 

std::cout << strlen((char*)c) << std::endl; 

Таким образом, strlen был моей первой проблемой. Но затем я попробовал следующее и обнаружил, что он вернул 11, 19, 27, 35. Разница между этими числами равна 8, поэтому на каком-то уровне происходит что-то правильное. Но почему это не возвращается 15, 15, 15, 15, (поскольку он возвращал 15 в вышеприведенном коде).

double d = 0.3; 
double d1 = 0.3; 
double d2 = 0.3; 
double d3 = 0.3; 

unsigned char *c_d = reinterpret_cast<unsigned char*> (&d); 
unsigned char *c_d1 = reinterpret_cast<unsigned char*> (&d1); 
unsigned char *c_d2 = reinterpret_cast<unsigned char*> (&d2); 
unsigned char *c_d3 = reinterpret_cast<unsigned char*> (&d3); 

std::cout << strlen((char*)c_d) << std::endl; 
std::cout << strlen((char*)c_d1) << std::endl; 
std::cout << strlen((char*)c_d2) << std::endl; 
std::cout << strlen((char*)c_d3) << std::endl; 

Итак, я посмотрел на адреса символов, и они есть.

0x28fec4 
0x28fec0 
0x28febc 
0x28feb8 

Теперь это имеет смысл, учитывая, что размер беззнаковое символ * в моей системе составляет 4 байта, но я думал, что правильное количество памяти будет выделено из актеров, в противном случае, похоже reinterpret_cast является довольно опасная вещь ... Кроме того, если я сделаю

for (int i = 0; i < 4; ++i) { 
    double d = 0.3; 

    unsigned char *c = reinterpret_cast<unsigned char*> (&d); 

    std::cout << strlen((char*)c) << std::endl; 
} 

Отпечатано 11, 11, 11, 11!

Итак, что происходит здесь, ясно, что память переписывается в местах, а реинтерпрет приведения не работает, поскольку я думал, что это будет (то есть я использую это неправильно). Если вы так долго используете строки на C++, иногда, когда вы возвращаетесь к массивам необработанных символов, вы забываете об этом.

Итак, я полагаю, что это вопрос из трех частей.

Почему перл изначально возвращал 15? Почему четыре звонка увеличились? Почему цикл вернулся 11, 11, 11, 11?

Спасибо.

+0

_casting_ не выделяет места для хранения. Использование 'reinterpret_cast' в основном говорит компилятору:« Эй, я знаю, что я делаю. Поверь мне ». Поэтому вам нужно знать, сколько мест хранения эти тезки могут указывать корректно. – Chad

+0

@ У меня такие ответы на мой вопрос лучше. Как я уже сказал в комментарии к выбранному ответу, я ожидал слишком многого от кастинга. Я знаю символ \ 0, хотя это было действительно поведение reinterpret_cast, и что именно он сделал, что меня немного смутило. –

ответ

11

strlen работы перебора массива, который он принимает переданные const char* точек на, пока он не находит char со значением 0. Это нуль-символ завершения, который автоматически добавляется в конце строки литералов. Байты, которые составляют представление значения вашего double, не заканчиваются нулевым символом.strlen будет просто продолжать идти мимо конца вашего double объекта до он находит байт со значением 0.

Рассмотрим строковый литерал "Hello". В памяти с совместимым выполнения набора символов ASCII, это будет храниться в виде следующих байтов (в шестнадцатеричном формате):

48 65 6c 6c 6f 00 

strlen бы читать через каждый из них, пока он не нашел байт со значением 0 и сообщать о том, сколько байтов, которые он видел до сих пор.

Стандарт IEEE 754 с двойной точностью представление 0.3 является:

3F D3 33 33 33 33 33 33 

Как вы можете видеть, нет байт со значением 0, так strlen просто не будет знать, когда остановиться.

Независимо от того, какое значение возвращает функция, вероятно, именно так, как она дошла до тех пор, пока не найдет 0 в памяти, но вы уже достигли неопределенного поведения, и поэтому любые догадки об этом бессмысленны.

+0

Я полагаю, что ожидал слишком много reinterpret_cast. Думаю, в моем сознании это сортировка памяти и прекращающего характера для меня. Мне никогда не приходило в голову, что это НЕ то, что должен делать reinterpret_cast. Это делает работу, остальное зависит от меня. –

+0

@Muckle_ewe 'reinterpret_cast' является самым опасным приложением. В нем говорится: «Поверьте мне, компилятор, я хочу, чтобы вы относились к этому как к другому типу». –

6

Ваша проблема заключается в использовании вами strlen((char*)c), потому что strlen ожидает указатель на строку символов с нулевым символом.

Кажется, что вы ожидаете какую-то «границу» между 8-м и 9-м байтами, так как первые 8 байтов были первоначально double.

Эта информация теряется после того, как вы отложили эту память до char*. Вы должны знать, сколько char s является действительным.

2

Пара вещей:

  1. sizeof(double), вероятно, не 4. Это обычно 8. Используйте оператор вместо жестко закодированы предположение.
  2. Указатель reinterpret_cast<unsigned char*>(&d) не указывает указатель на строку с нулевым символом. strlen работает путем итерации, пока не найдет нуль. Там вы находитесь в неопределенном поведении.
Смежные вопросы