Когда и как разрешено преобразование в указатель на символ?

Мы можем посмотреть на представление объекта типа T путем преобразования 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* и какие преобразования это влечет за собой? Я вызываю неопределенное поведение, как только произнесу? Могу ли я бросить любой T* типа как это? На что я могу положиться при этом?

20

Решение

Какой из приведений позволяет мне преобразовать int* для unsigned char*?

Это бросок в стиле C в этом случае такой же, как reinterpret_cast<unsigned char*>,

Могу ли я разыграть любой тип T * как этот

И да и нет. Часть да: вы можете безопасно привести любой тип указателя к char* или же unsigned char* (с соответствующими const и / или volatile классификаторы). Результат определяется реализацией, но это законно.

Часть no: стандарт явно разрешает char* а также unsigned char* как тип цели. Однако вы не можете (например) безопасно разыграть double* для int*, Сделайте это, и вы пересекли границу от поведения, определенного для реализации, до поведения, не определенного. Это нарушает строгое правило наложения имен.

16

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

Ваши актеры отображаются на:

unsigned char* cp = reinterpret_cast<unsigned char*>(&x);

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

Здесь нет ничего особенного — этот метод проверки внутреннего представления действителен для любого типа данных.

C ++ 03 5.2.10.7: Указатель на объект может быть явно преобразован в указатель на объект другого типа. За исключением того, что преобразование r-значения типа «указатель на T1» в тип «указатель на T2» (где T1 и T2 являются типами объектов и где требования к выравниванию T2 не являются более строгими, чем требования к T1) и обратно к его исходному типу, дает исходное значение указателя, результат такого преобразования указателя не определен.

Это говорит о том, что приведение приводит к неуточненное поведение. Но, прагматически говоря, приведение от любого типа указателя к char* всегда позволит вам изучить (и изменить) внутреннее представление ссылочного объекта.

5

Приведение в стиле C в этом случае эквивалентно reinterpret_cast. Стандарт описывает семантику в 5.2.10. В частности, в пункте 7:

«Указатель на объект может быть явно преобразован в указатель на
другой тип объекта. 70 Когда значение v типа «указатель на T1» равно
преобразуется в тип «указатель на cvT2», в результате
static_cast<cvT2*>(static_cast<cvvoid*>(v)) если оба T1 и T2
Типы стандартного макета (3.9) и требования выравнивания T2
не более строгие, чем у T1. Преобразование значения типа «указатель на
T1 »к типу« указатель на T2 »(где T1 и T2 являются типами объектов и
где требования по выравниванию T2 не являются более строгими, чем требования
T1) и обратно в исходный тип возвращает исходное значение указателя.
Результат любого другого такого преобразования указателя не определен. «

Что это означает в вашем случае, требования по выравниванию выполняются, а результат не уточняется.

3

Поведение реализации в вашем примере является атрибутом endianness вашей системы, в этом случае ваш CPU немного порядковый.
О типе литья, когда вы бросаете int* в char* все, что вы делаете, говорит компилятору интерпретировать что cp указывает на символ, поэтому он будет читать только первый байт и интерпретировать его как символ.

2

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

Но, конечно же, способ формирования последовательности зависит от того, как разложенный тип представлен в памяти, и это выходит за рамки спецификаций C ++.

Тем не менее, за исключением очень патологических случаев, вы можете ожидать, что представление будет одинаковым для всего кода, созданного одним и тем же компилятором для всех машин одной и той же платформы (или семейства), и вы не должны ожидать одинаковых результатов на разных платформах. ,

В общем, следует избегать выражения отношения между размерами шрифта как «предопределенного»:
в вашем образце вы предполагаете sizeof(int) == 4*sizeof(char)Это не всегда так.

Но всегда верно, что sizeof (T) = N * sizeof (char), следовательно, все, что T всегда можно рассматривать как целое число char-s

1

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

Затем вы читаете побайтную область памяти; до тех пор, пока вы не измените его, это просто нормально. Конечно, результат того, что вы видите, во многом зависит от платформы: подумайте о порядке байтов, размеру слова, заполнении и так далее.

0

Просто измените порядок байтов, тогда он становится

00 00 01 ff

Что составляет 256 (01) + 255 (ff) = 511

Это потому что твой платфом немного порядковый.

0
По вопросам рекламы [email protected]