#c
#c
Вопрос:
Может ли это приведение завершиться неудачей и когда?
long x=-1;
long y = (long)(void*)x;
assert(x==y);
Более конкретно, как определить, подходит ли приведенное выше приведение во время компиляции.
Комментарии:
1. Просто догадываюсь: это для
pthread_create
?
Ответ №1:
Более переносимый способ (в стандартном варианте C99) заключается в том, чтобы #include <stdint.h>
затем приводить указатели к intptr_t
(и обратно). Этот целочисленный тип гарантированно будет размером с указатель.
Комментарии:
1. Обратите внимание, что OP приводит integer-> pointer-> integer, который в моем понимании стандарта немного менее определен, чем pointer-> integer-> указатель.
2. Я забыл детали, и я думаю, что стандарты изменились в этом аспекте. На практике указатели на функции являются указателями, теоретически это не совсем так.
3. @Basile: разница здесь не в теории и практике — скорее, стандарт C должен справляться с существующими архитектурами, где указатели кода и данных имеют представления разных размеров (например, far и near); даже сегодня существуют микроконтроллеры без плоской модели памяти, и вам даже не нужно искать экзотические архитектуры — любой процессор x86 все еще может использоваться в реальном режиме
4. Я думаю, что комитет POSIX в какой-то момент рассматривал возможность иметь
dlsym
и другую функцию (возможноdlfsym
?) для работы с указателями на функции, размер которых отличается от размера указателей на данные, но они отказались от этой идеи, так что, по крайней мере, в области POSIX, практически говоря, указатели на функции имеют тот же размер, что и указатели на данные.
Ответ №2:
Переносимо нет. Я знаю даже реализацию, в которой это приведет к сбою. реальный режим x86 с моделями памяти крошечного, малого и среднего размера. long
имеет 32 бита, а указатели — 16 бит. Другие микроконтроллеры с гарвардской архитектурой, вероятно, тоже выйдут из строя.
Ответ №3:
Насколько я помню, это не приведет к сбою, учитывая, что вы исправили тип y на (long)(void*)
.
Комментарии:
1. Хм? «(long)(void*)» не является типом. Это серия из двух приведений, сначала приведение к
void*
, а затем приведение кlong
.
Ответ №4:
Это может привести к сбою в архитектурах, где long int и указатель имеют разный размер. Некоторые 64-битные архитектуры имеют такое поведение. Как говорит Basile, используйте вместо этого intptr_t, если это доступно в вашей системе.
Вы можете обнаружить эту ситуацию во время компиляции следующим образом:
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]
STATIC_ASSERT((sizeof(void*) > sizeof(long)), incompatible_pointer_size);
Комментарии:
1. Нет,
sizeof
не может отображаться в выражениях препроцессора, поэтому приведенное выше, к сожалению, неверно.2. Верно в отношении препроцессора, но идея, которая
sizeof (void*) > sizeof (long)
может быть правдой, является важным сообщением здесь.3. @BasileStarynkevitch прав, вы должны использовать трюк со статическим утверждением. Я отредактирую ответ.
4. Это уже отредактированный?