Безопасно выбрасывайте void * в int

0

Если приложение скомпилировано для получения изображения 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?
  • 0
    Параметры void* не предназначены для прямой передачи данных. Просто передайте адрес целых чисел вместо значения, и все в порядке. То, что вы пытаетесь, никогда не будет работать должным образом и является основной причиной медленного внедрения 64-битных систем в мире x86.
  • 0
    Я знаю, что могу, но мой вопрос о таком случае - есть ли место для UB? Я должен предположить, что кто-то мог пройти НИЧЕГО за пустотой * - как мне предотвратить UB?
Показать ещё 5 комментариев
Теги:
casting
int
undefined-behavior
void-pointers

2 ответа

5
Лучший ответ

Да, это может привести к неопределенному поведению. Если вы используете intptr_t вместо этого, то нет неопределенного поведения.

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

Во втором примере у вас есть мишмаш. Вы хотите использовать либо intptr_t, либо void * который указывает на int. Не intptr_t * или uintptr_t *.

Мое предпочтительное решение заключается в том, что user_data всегда указывает на данные; и тип указываемых данных определяется вызываемым обработчиком или другим параметром.

  • 0
    Извините, мне было неясно. Вы должны использовать intptr_t или uintptr_t в качестве типа параметра для handler . Вы все еще можете заставить UB делать то, что вы есть сейчас (потому что uintptr_t может быть шире диапазона допустимых значений для void * )
  • 0
    Я не могу изменить подпись обработчика, потому что это обратный вызов. Я должен разыграть void * в ENUM, однако внутри. Также, если uintptr_t шире, это нормально. пустота * подойдет к нему
Показать ещё 1 комментарий
0

Этот бит:

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 *, и этот прилив не ухудшит ситуацию.

  • 0
    Это не я! ; Я просто невинная жертва состояния дел и должна как-то с этим справиться. Я знаю, что это безобразно! Кроме того, вы не правы в том, что «учитывая, что WSO_1 на самом деле является перечислением, и я не знаю ни одной платформы, где void * на самом деле меньше, чем перечисление, вы должны быть в порядке» - это на самом деле моя проблема, что void * может быть больше чем ENUM
  • 0
    @AB_ У вас есть контроль над handler судя по вопросу
Показать ещё 13 комментариев

Ещё вопросы

Сообщество Overcoder
Наверх
Меню