Почему / когда использовать 'intptr t' для литья типов в C?

у меня есть вопрос относительно использования intptr_t и long int. Я заметил, что приращение адресов памяти (например, через арифметику ручного указателя) отличается по типу данных. Например, увеличение указателя char добавляет 1 к адресу памяти, в то время как увеличение указателя int добавляет 4, 8 для double, 16 для long double и т. д...

сначала я сделал что-то вроде этого:

char myChar, *pChar;
float myFloat, *pFloat;

pChar = &myChar;
pFloat = &myFloat;

printf( "pChar:  %dn", ( int )pChar );
printf( "pFloat: %dn", ( int )pFloat );

pChar++;
pFloat++;

printf( "and then after incrementing,:nn" );
printf( "pChar:  %dn", (int)pChar );
printf( "pFloat:    %dn", (int)pFloat );

который скомпилирован и выполнен просто отлично, но XCode дал мне предупреждения для моего typecasting: "приведение от указателя к целому числу разного размера."

после некоторого googling и binging (это последнее слово еще?), Я видел, что некоторые люди рекомендуют использовать intptr_t:

#include <stdint.h>

...

printf( "pChar:  %ldn", ( intptr_t )pChar );
printf( "pFloat: %ldn", ( intptr_t )pFloat );

, который фактически устраняет ошибки. Поэтому, подумал я, отныне я должен использовать intptr_t для приведения типов указателей... Но затем после некоторого беспокойства я обнаружил, что могу решить проблему, просто заменив int С long int:

printf( "pChar:  %ldn", ( long int )pChar );
printf( "pFloat: %ldn", ( long int )pFloat );

Итак, мой вопрос, почему intptr_t полезно, и когда он используется? В данном случае это кажется излишним. Очевидно, что адреса памяти для myChar и myFloat были слишком большими, чтобы поместиться в int... так что приведение их в long ints решил проблему.

это то, что иногда адреса памяти слишком велики для long int так же? Теперь, когда я думаю об этом, я думаю, что это возможно, если у вас 4 ГБ ОЗУ, в этом случае память адреса могут превышать 2^32-1 (максимальное значение для длинных ints без знака...) но C был создан задолго до того, как это можно было себе представить, верно? Или они настолько прозорливы?

спасибо!

4 ответов


вот в чем дело: на некоторых платформах, int это правильный размер, но на других, long - это правильный размер. Откуда вы знаете, какой из них вам следует использовать? А ты-нет. Возможно, вы правы, но стандарт не дает никаких гарантий относительно того, какой из них будет (если он есть). Таким образом, стандарт предоставляет тип, который определяется как правильный размер, независимо от того, на какой платформе вы находитесь. Где раньше приходилось писать:

#ifdef PLATFORM_A
  typedef long intptr;
#else
  typedef int intptr;
#endif

теперь вы просто пиши:

#include <stdint.h>

и он охватывает так много других случаев. Представьте себе специализацию фрагмента выше для каждую платформу ваш код выполняется на.


intptr_t - Это новое изобретение, созданное после того, как были придуманы 64-битные и даже 128-битные адреса памяти.

Если вы когда-нибудь необходимо привести указатель в целочисленный тип,всегда использовать intptr_t. Выполнение чего-либо еще вызовет ненужные проблемы для людей, которым необходимо перенести ваш код в будущем.

потребовалось много времени, чтобы сгладить все ошибки с этим в таких программах, как Mozilla/Firefox, когда люди хотели скомпилировать его на 64-бит Линукс.


во-первых,intptr_t только для указателей данных (не функций) и не гарантируется существование.

тогда, нет, вы не должны использовать его для печати. The %p для этого. Вам просто нужно бросить указатель на (void*) и там вы идете.

это также не хорошо для арифметики / доступа к отдельным байтам. Бросьте в .

intptr_t действительно для редких случаев, когда вам нужно интерпретировать указатели как целые числа (которые они на самом деле нет). Не делай этого, если не должен.--6-->


вы могли бы сделать вашу жизнь проще с помощью p спецификатор преобразования:

printf("%p\n", (void *)foo);

кроме того, портативный способ печати переменной типа (u)intptr_t использовать PRI*PTR макросы от inttypes.h; следующее эквивалентно использованию p на моей платформе (32-бит):

printf("%08" PRIxPTR "\n", (uintptr_t)(void *)foo);

бросания в void * необходимы для полной переносимости, но могут быть опущены на платформах с равномерными представлениями указателей.