Подтвердить что ты не робот

Что такое использование intptr_t?

Я знаю, что это целочисленный тип, который может быть добавлен в/из указателя без потери данных, но зачем мне это делать? Какое преимущество имеет целочисленный тип над void* для удержания указателя и THE_REAL_TYPE* для арифметики указателя?

ИЗМЕНИТЬ
Вопрос, обозначенный как "уже заданный вопрос", не отвечает на этот вопрос. Вопрос в том, что использование intptr_t в качестве общей замены для void* является хорошей идеей, и ответы там, кажется, "не используют intptr_t", поэтому мой вопрос по-прежнему действителен: что было бы полезно использовать случай для intptr_t?

4b9b3361

Ответ 1

Основная причина: вы не можете выполнять побитовое управление на void *, но вы можете сделать то же самое на intptr_t.

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

Однако для побитовых операций наилучшим подходом является использование аналога unsigned, uintptr_t.

Как упоминалось в другом ответе @chux, сравнение указателей - еще один важный аспект.

Кроме того, FWIW, согласно стандарту C11, §7.20.1.4,

Эти типы являются необязательными.

Ответ 2

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

Вы можете делать одни и те же вещи, работая в терминах char *, но не все, и результат выглядит как pre-Ansi C.

Не весь код управления памятью использует uintptr_t - в качестве примера код ядра BSD определяет vm_offset_t со схожими свойствами. Но если вы пишете, например. пакет debug malloc, зачем изобретать свой собственный тип?

Это также полезно, когда у вас есть% p, доступное в вашем printf, и пишут код, который должен печатать интегральные переменные размера указателя в шестнадцатеричном виде на разных архитектурах.

Я нахожу intptr_t более менее полезным, за исключением, возможно, как way station при кастинге, чтобы избежать предупреждения о страхе об изменении размера подписей и целого числа в том же самом произведении. (Написание переносимого кода, который проходит -Wall -Werror на всех соответствующих архитектурах, может быть битвой.)

Ответ 3

Сравнение указателей на равенство не является проблемой.
Другие операции сравнения, такие как >, <=, могут быть UB. C11dr §6.5.8/5 Операторы отношения.
Поэтому сначала конвертируйте в intptr_t.

[Изменить] Новый пример: Сортировка массива указателей по значению указателя.

int ptr_cmp(const void *a, const void *b) {
  intptr_t ia = (intptr) (*((void **) a));
  intptr_t ib = (intptr) (*((void **) b));
  return (ia > ib) - (ia - ib);
}

void *a[N];
...
qsort(a, sizeof a/sizeof a[0], sizeof a[0], ptr_cmp);

[Бывший пример] Пример использования: проверьте, содержит ли указатель массив указателей.

#define N  10
char special[N][1];

// UB as testing order of pointer, not of the same array, is UB.
int test_special1(char *candidate) {
  return (candidate >= special[0]) && (candidate <= special[N-1]);
}

// OK - integer compare
int test_special2(char *candidate) {
  intptr_t ca = (intptr_t) candidate;
  intptr_t mn = (intptr_t) special[0];
  intptr_t mx = (intptr_t) special[N-1];
  return (ca >= mn) && (ca <= mx);
}

 ---

[изменить]

Как прокомментировано @M.M, вышеуказанный код может работать не так, как предполагалось. Но, по крайней мере, это не UB. - просто функциональность без переносимости - grrrrrrrrr. Я надеялся использовать это для решения этой проблемы.

Ответ 4

В 64-битной системе целое число составляет 4 байта (32 бит), а указатель - 8 байтов (64 бит).

intptr_t переводит указатели на size_t. С помощью этого безопасного метода вы можете написать кросс-платформенный совместимый код.

Например:

size_t ref(void *pointer) {
    return (size_t)(intptr_t)pointer
}