Какая польза от intptr_t?

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

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

6 ответов

Решение

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

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

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

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

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

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

Там также семантическое соображение.

void* должен указывать на что-то. Несмотря на современную практичность, указатель не является адресом памяти. Хорошо, обычно / вероятно / всегда (!) Содержит один, но это не число. Это указатель. Это относится к вещи.

intptr_t не. Это целочисленное значение, которое безопасно конвертировать в / из указателя, чтобы вы могли использовать его для античных API, упаковав его в pthread аргумент функции и тому подобное.

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

В конечном счете, почти все может быть целым числом (помните, ваш компьютер работает с числами!). Указатели могли быть целыми числами. Но это не так. Они указатели, потому что они предназначены для различного использования. И, теоретически, они могут быть чем-то отличным от цифр.

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

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

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

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

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

Какая польза от intptr_t?

Пример использования: сравнение заказов.
Сравнение указателей на равенство не является проблемой.
Другие операции сравнения, такие как >, <= может быть 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);
}

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

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

  • В x86-64 вы можете использовать старшие биты 16/7 для данных, но вы должны сделать расширение знака вручную, чтобы сделать указатель каноническим, потому что в настоящее время у него нет флага для игнорирования старших битов, как в ARM . Так, например, если у вас есть char* address тогда вам нужно будет сделать это перед разыменованием

            char* pointer = (char*)((intptr_t)address << 16 >> 16);
    
  • 32-битный движок Chrome V8 использует оптимизацию smi (маленькое целое число), где младший бит обозначает тип

                        |----- 32 bits -----|
    Pointer:    |_____address_____w1| # Address to object, w = weak pointer
    Smi:        |___int31_value____0| # Small integer
    

    Поэтому, когда младший бит указателя равен 0, он будет сдвинут вправо, чтобы получить исходный 31-битный знаковый int.

            int v = (intptr_t)address >> 1;
    

Для получения дополнительной информации прочтите

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

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

Например:

size_t ref(void *pointer) {
    return (size_t)(intptr_t)pointer;
}
Другие вопросы по тегам