2014-11-07 4 views
0

Если приложение скомпилировано для получения изображения x32, то в зависимости от типа целочисленного типа архитектура может иметь ширину в 16 бит, ширину 32 или более бит или что-то большее, чем 2 байта. Размер void* будет 4 (на x32 всегда 4 ???). Это означало бы, что переход int к void* это хорошо, но если окажется, что на данной архитектуре void* шире int (который только гарантированно по меньшей мере, 2 байта по Стандарту), чем в лицеSafely cast void * to int

C Стандарт n1124 § 6.3.2.3 указатели

5 целое число может быть преобразовано в любой тип указателя. За исключением случаев, ранее указано, результат зависит от реализации, возможно, не быть правильно выровнены, может не указывать на сущности ссылочного типа, и может быть ловушка representation.56)

6 Любой тип указателя может преобразуется в целочисленный тип. За исключением, как указано ранее , результат определяется реализацией. Если результат не может быть представлен в целочисленном типе, поведение равно undefined. Результат не должен быть в диапазоне значений любого целочисленного типа .

литья void* к 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? 

Если это правильно, что возможность для носовых демонах открыт - как же я тогда пишу thiss бросание безопасно? Могу ли я использовать 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 *' параметры * не * предназначены для передачи данных напрямую. Просто передайте целые числа * address * вместо значения, и вы в порядке. То, что вы пытаетесь, никогда не будет работать должным образом и является основной причиной медленного внедрения 64-бит в мире x86. – filmor

+0

Я знаю, что могу, но мой вопрос о таком случае - есть ли место для UB? Я должен предположить, что кто-то мог пропустить НИЧЕГО за void * - как я могу предотвратить UB? – 4pie0

+0

в цитируемой части четко сказано, что это * реализация определена *. –

ответ

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 шире, это нормально. void * уложится в него – 4pie0

+0

Вещь, которую вы бросаете на 'void *', может не входить в 'void *'. Если вы не можете изменить функцию, тогда правильная вещь - фактически передать указатель; не делайте reinterpret_cast. –

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); 

Клиент имеет уже попал в сомнительные области, когда он перебросил перечисление в пустоту *, и этот прилив не ухудшит ситуацию.

+0

Это не я! ; p Я просто невинная жертва состояния дел и должен каким-то образом справиться с этим. Я знаю, что это уродливо! Также вы ошибаетесь в том, что «WSO_1 на самом деле является перечислением, и я не знаю какой-либо платформы, где void * на самом деле меньше, чем перечисление, вы должны быть в порядке» - на самом деле это моя проблема, что void * может быть больше, чем ENUM – 4pie0

+0

@AB_ У вас есть контроль над «обработчиком», хотя судя по вопросу –

+0

Я не могу изменить его подпись – 4pie0