C ++. reinterpret_cast от двойного до беззнакового символа *

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

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

Итак, вот некоторый код для преобразования из массива в тип char, или, по крайней мере, я подумал, что это именно то, что он делает. Проблема была в том, что он возвращал 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 ++, иногда, когда вы возвращаетесь к необработанным массивам символов, вы забываете об этом.

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

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

Благодарю.

5

Решение

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 в памяти, но вы уже столкнулись с неопределенным поведением, и поэтому делать какие-либо предположения об этом бессмысленно.

11

Другие решения

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

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

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

6

Пара вещей:

  1. sizeof(double) вероятно, не 4. Обычно 8. Используйте оператор вместо жестко заданного предположения.
  2. Указатель reinterpret_cast<unsigned char*>(&d) не указывает указатель на завершенную нулем «строку». strlen работает путем итерации, пока не найдет ноль. Вы в неопределенном поведении там.
2
По вопросам рекламы [email protected]