Почему / когда использовать `intptr_t` для приведения типов в C? - PullRequest
43 голосов
/ 13 июня 2011

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

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

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

pChar = &myChar;
pFloat = &myFloat;

printf( "pChar:  %d\n", ( int )pChar );
printf( "pFloat: %d\n", ( int )pFloat );

pChar++;
pFloat++;

printf( "and then after incrementing,:\n\n" );
printf( "pChar:  %d\n", (int)pChar );
printf( "pFloat:    %d\n", (int)pFloat );

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

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

#include <stdint.h>

...

printf( "pChar:  %ld\n", ( intptr_t )pChar );
printf( "pFloat: %ld\n", ( intptr_t )pFloat );

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

printf( "pChar:  %ld\n", ( long int )pChar );
printf( "pFloat: %ld\n", ( long int )pFloat );

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

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

Спасибо!

Ответы [ 4 ]

45 голосов
/ 13 июня 2011

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

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

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

36 голосов
/ 13 июня 2011

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

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

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

#include <stdint.h>

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

10 голосов
/ 13 июня 2011

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

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

Это также не годится для арифметики / доступа к отдельным байтам.Вместо этого приведите (unsigned char*).

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

9 голосов
/ 13 июня 2011

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

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

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

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

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

...