Когда и как разрешено преобразование в char?
Мы можем посмотреть на представление объекта типа T
путем преобразования a T*
, который указывает на этот объект в char*
. По крайней мере, на практике:
int x = 511;
unsigned char* cp = (unsigned char*)&x;
std::cout << std::hex << std::setfill('0');
for (int i = 0; i < sizeof(int); i++) {
std::cout << std::setw(2) << (int)cp[i] << ' ';
}
Это выводит представление 511
в моей системе: ff 01 00 00
.
Существует (конечно) какое-то определенное поведение, определенное здесь. Какая из бросок позволяет мне преобразовать int*
в unsigned char*
и какие преобразования делают это приведение? Я вызываю поведение undefined, как только я бросаю? Можно ли использовать любой тип T*
следующим образом? На что я могу положиться при этом?
Ответы
Ответ 1
Какое из приводов позволяет мне преобразовать int*
в unsigned char*
?
Этот C-стиль в этом случае совпадает с reinterpret_cast<unsigned char*>
.
Можно ли использовать любой тип T * следующим образом?
Да и нет. Часть да. Вы можете безопасно использовать любой тип указателя в char*
или unsigned char*
(с соответствующими квалификаторами const
и/или volatile
). Результат определяется реализацией, но он легален.
Нет части: стандарт явно допускает char*
и unsigned char*
в качестве целевого типа. Однако вы не можете (например) безопасно применить double*
к int*
. Сделайте это, и вы пересекли границу от поведения, определенного при реализации, до поведения undefined. Это нарушает правило строгого сглаживания.
Ответ 2
Ваши литые карты:
unsigned char* cp = reinterpret_cast<unsigned char*>(&x);
Основное представление int
определяется реализацией, и просмотр его в виде символов позволяет вам изучить это. В вашем случае это 32-битный маленький endian.
Здесь нет ничего особенного - этот метод проверки внутреннего представления действителен для любого типа данных.
С++ 03 5.2.10.7: Указатель на объект может быть явно преобразован в указатель на объект другого типа. За исключением того, что преобразование rvalue типа "указатель на T1" в тип "указатель на T2" (где T1 и T2 являются типами объектов и где требования к выравниванию T2 не более строгие, чем требования T1) и обратно к исходному типу исходное значение указателя, результат такого преобразования указателя не указан.
Это говорит о том, что приведение приводит к неопределенному поведению. Но прагматично говоря, кастинг от любого типа указателя до char*
всегда позволит вам изучить (и изменить) внутреннее представление ссылочного объекта.
Ответ 3
Приведение в стиле C в этом случае эквивалентно reinterpret_cast. Стандарт описывает семантику в 5.2.10. В частности, в пункте 7:
"Указатель на объект может быть явно преобразован в указатель на другой тип объекта .70 Когда prvalue v типа" указатель на T1 "является преобразованный в тип" указатель на cvT2 ", результатом является static_cast<cvT2*>(static_cast<cvvoid*>(v))
, если оба T1 и T2 равны стандартными форматами (3.9) и требованиями к выравниванию T2 являются не более строгие, чем у T1. Преобразование указателя типа" указатель на T1 "к типу" указатель на T2 "(где T1 и T2 являются типами объектов и где требования к выравниванию T2 не являются строгими, чем требования к T1) и обратно к исходному типу дает исходное значение указателя. Результат любого другого такого преобразования указателя не указан."
Что это означает в вашем случае, требования к выравниванию удовлетворяются, и результат не указан.
Ответ 4
Поведение реализации в вашем примере - это атрибут endianness вашей системы, в этом случае ваш процессор немного ориентирован.
О типе casting, когда вы отбрасываете int*
в char*
, все, что вы делаете, говорит компилятору интерпретировать, что cp
указывает как char, поэтому он будет читать только первый байт и интерпретировать это как символ.
Ответ 5
Передача между указателями сама по себе всегда возможна, поскольку все указатели - это не что иное, как адреса памяти, и любой тип в памяти всегда можно рассматривать как последовательность байтов.
Но, конечно, способ формирования последовательности зависит от того, как представленный тип разбиения представлен в памяти, и что из объема спецификаций С++.
Тем не менее, если только из очень патологических случаев, вы можете ожидать, что представление будет одинаковым для всего кода, созданного одним и тем же компилятором для всех машин одной и той же платформы (или семейства), и вы не должны ожидать таких же результатов на разных платформах.
В общем, одна вещь, которую следует избегать, заключается в том, чтобы выразить соотношение между размерами типа как "предопределенное":
в вашем примере вы принимаете sizeof(int) == 4*sizeof(char)
: это необязательно всегда верно.
Но всегда верно, что sizeof (T) = N * sizeof (char), следовательно, любой T всегда можно рассматривать как целое число char -s
Ответ 6
Если у вас нет оператора трансляции, то актер просто говорит "видеть" эту область памяти по-другому. Я бы сказал, ничего фантастического.
Затем вы читаете побайтовые области памяти; до тех пор, пока вы его не меняете, все нормально. Разумеется, результат того, что вы видите, сильно зависит от платформы: подумайте о контенте, размере слова, дополнении и т.д.
Ответ 7
Просто измените порядок байтов, затем он станет
00 00 01 ff
Что такое 256 (01) + 255 (ff) = 511
Это потому, что ваш плафом немного велик.