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

У меня есть вопрос относительно использования 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Решил проблему.

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

Спасибо!

5 ответов

Решение

Вот в чем дело: на некоторых платформах 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-битном Linux.

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

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

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

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

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

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

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

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

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

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

Это не верно. И это еще хуже

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

Может быть, адреса памяти иногда слишком велики и для long int?

Да, ничто не требует того же размера, что и , например, в 64-битной версии Windows 32-битная, но указатели 64-битные, или некоторые 128-битные архитектуры в будущем могут иметь 128-битные указатели, но короче.longтип. Поэтому это неверно

Очевидно, что адреса памяти для myCharи myFloatбыли слишком большими, чтобы поместиться в int... поэтому приводим их к long intэто решило проблему

Это решает проблему только на некоторых платформах. Вы должны использовать PRIdPTRили PRIiPTRдля . Правильный способ такой

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

Но зачем вам нужно печатать десятичные значения указателей? Обычно они должны быть напечатаны в шестнадцатеричном формате . Кроме того, в большинстве случаев предпочтительнее, особенно при выполнении побитовой арифметики над указателями (например, для помеченных указателей или связанного списка XOR), так что это лучше.

      printf("pChar:  %" PRIxPTR "\n", (uintptr_t)pChar);
printf("pFloat: %" PRIxPTR "\n", (uintptr_t)pFloat);

почему это полезно и когда его следует использовать

Есть очень мало случаев, когда вам нужно, а неuintptr_t. Посмотрите, в чем польза intptr_t?для некоторых примеров

Другие вопросы по тегам