Всегда ли безопасно преобразовывать целочисленное значение в void* и обратно в POSIX?

Этот вопрос почти дублирует некоторые другие, которые я нашел, но этот конкретно касается POSIX и очень распространенного примера в pthreads, с которым я сталкивался несколько раз. Меня в основном интересует текущее состояние дел (т.е. C99 и POSIX.1-2008 или более поздние версии), но любая интересная историческая информация, конечно, тоже интересна.

Вопрос в основном сводится к тому, будет ли b всегда принимать то же значение, что и a в следующем коде:

long int a = /* some valid value */
void *ptr = (void *)a;
long int b = (long int)ptr;

Я знаю, что обычно это работает, но вопрос в том, правильно ли это делать (т.е. гарантируют ли стандарты C99 и/или POSIX, что это будет работать).

Когда дело доходит до C99, кажется, что нет, у нас есть 6.3.2.3:

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

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

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

Однако все еще возможно, что стандарт POSIX позволяет это.

У меня нет большого желания использовать void* в качестве места хранения для любой переменной (я нахожу это довольно уродливым, даже если POSIX должен это разрешить), но я чувствую, что должен спросить из-за распространенного примера использования функции pthreads_create, где аргументом start_routine является целое число, и оно передается как void* и преобразуется в int или long int в функции start_routine. Например, на этой manpage есть такой пример (см. ссылку для полного кода):

//Last argument casts int to void *
pthread_create(&tid[i], NULL, sleeping, (void *)SLEEP_TIME);
/* ... */
void * sleeping(void *arg){
    //Casting void * back to int
    int sleep_time = (int)arg;
    /* ... */
}

Я также видел подобный пример в учебнике (An Introduction to Parallel Programming by Peter S. Pacheco). Учитывая, что это, похоже, обычный пример, используемый людьми, которые должны знать этот материал гораздо лучше меня, я задаюсь вопросом, не ошибаюсь ли я, и действительно ли это безопасная и переносимая вещь, которую нужно делать.

23
задан Quantumboredom 19 October 2011 в 18:47
поделиться