Если приложение скомпилировано для получения изображения x32, то в зависимости от типа целочисленного типа архитектура может иметь ширину 16 бит, ширину 32 или более бит или что-то большее, чем 2 байта. Размер void*
будет 4 (на x32 всегда 4???). Это означало бы, что передача int
в void*
прекрасна, но если окажется, что на данной архитектуре void*
шире, чем int
(который гарантируется, по стандарту, как минимум, 2 байта), чем перед
C Стандарт n1124 § 6.3.2.3 Указатели
5 Целое число может быть преобразовано в любой тип указателя. За исключением, как указано ранее, результат определяется реализацией, может быть неправильно выровнен, может не указывать на объект ссылочного типа и может быть ловушкой. 56)
6 Любой тип указателя может быть преобразован в целочисленный тип. За исключением случаев, указанных ранее, результат определяется реализацией. Если результат не может быть представлен в целочисленном типе, поведение не определено. Результат не обязательно должен находиться в диапазоне значений любого целочисленного типа.
casting void*
to int
может приводить к неопределенному поведению в следующем фрагменте.
typedef enum tagENUM
{
WSO_1,
WSO_2,
//...
WSO_COUNT
} ENUM;
/* I cannot change handler signature because this is callback. I have to cast void*
* to ENUM however inside */
void handler( int i, int j, void *user_data)
{
ENUM mOperation;
mOperation = (ENUM)reinterpret_cast<int>(user_data);
}
// somewhere
handler( 1, 2, (void*)WSO_1); // UB? We can imagine that someone passes to handler
// (void*)WSO_131072 which don't fit into 16 bits
// So is there a place opened for UB?
Если это правильно, то открыта возможность для носовых деамонов - как я могу тогда написать эти данные безопасно? Могу ли я использовать intptr_t
чтобы убедиться, что результат будет соответствовать?
void handler( int i, int j, void *user_data)
{
ENUM mOperation;
uintptr_t p_mOperation = reinterpret_cast<uintptr_t>( user_data);
if ( p_mOperation > WSO_COUNT ) {
send_error(conn, 500, http_500_error, "Error: %s", strerror(ERRNO));
return;
}
mOperation = static_cast<ENUM_WS_OPERATION>( p_mOperation); // now safe?
Да, это может привести к неопределенному поведению. Если вы используете intptr_t
вместо этого, то нет неопределенного поведения.
Однако, как правило, вы можете переписать свой код так, чтобы указатель указывал на предполагаемое целое число, а не на то, чтобы быть направленным на него.
Во втором примере у вас есть мишмаш. Вы хотите использовать либо intptr_t
, либо void *
который указывает на int
. Не intptr_t *
или uintptr_t *
.
Мое предпочтительное решение заключается в том, что user_data
всегда указывает на данные; и тип указываемых данных определяется вызываемым обработчиком или другим параметром.
intptr_t
или uintptr_t
в качестве типа параметра для handler
. Вы все еще можете заставить UB делать то, что вы есть сейчас (потому что uintptr_t
может быть шире диапазона допустимых значений для void *
)
Этот бит:
mOperation = (ENUM)reinterpret_cast<int>(user_data);
предлагает вам потерять контроль над тем, что вы делаете. Вы делаете двойной бросок, который всегда свидетельствует о плохих вещах (как если бы reinterpret_cast еще не указывал это).
Из этого не ясно, что вы пытаетесь сделать. Конечно (void *) является неприемлемым броском для передачи значений, которые не являются данными. Это для передачи указателей на произвольные буферы данных, где каждый надеется, что вещь на другом конце может работать из данных, что ей нужно делать с этим.
Итак, это:
handler( 1, 2, (void*)WSO_1);
просто неправильно.
Однако, учитывая, что WSO_1
на самом деле является enum
, и я не знаю какой-либо платформы, где void *
на самом деле меньше, чем перечисление, вы должны быть в порядке, чтобы сделать
mOperation = reinterpret_cast<ENUM>(user_data);
Клиент уже перешел в сомнительные области, когда он перечислил enum в void *, и этот прилив не ухудшит ситуацию.
handler
судя по вопросу
void*
не предназначены для прямой передачи данных. Просто передайте адрес целых чисел вместо значения, и все в порядке. То, что вы пытаетесь, никогда не будет работать должным образом и является основной причиной медленного внедрения 64-битных систем в мире x86.