Существует ли целочисленный тип того же размера, что и указатель? Гарантировано на всех микроархитектурах?
sizeof (int) == sizeof (void*)?
Ответы (10)
Согласно этой странице Википедии, в C99 ваш заголовок stdint.h может объявить intptr_t и uintptr_t, но тогда это, конечно, требует
- C99
- Разработчик компилятора, решивший реализовать эту необязательную часть стандарта.
Так что в целом я думаю, что это сложно.
u
)intptr_t
и обратно (при условии, что память, на которую он ссылается, все еще действительна), и что нулевой указатель преобразуется в 0. В разумных архитектурах это будет просто адрес памяти, но это ни в коем случае означает гарантированный (например, приведение может случайным образом переставлять биты, нет гарантии, что вы можете вывести выравнивание из представления uintptr_t
, арифметика указателя не должна работать...).
- person tc.; 11.06.2012
int x = 0; if ((void*)x) { ... }
определяется реализацией. Было бы действительно странно, если бы поведение резко изменилось в тот момент, когда компилятор решил, что что-то больше не является постоянным.
- person tc.; 19.06.2021
Проще говоря, нет. Не гарантируется для всех архитектур.
Мой вопрос: почему? Если вы хотите выделить тип, достаточно большой для хранения void*
, лучше всего выделить (как ни странно :-) тип void*
. Зачем нужно помещать его в int
?
РЕДАКТИРОВАТЬ: Основываясь на ваших комментариях к дублирующемуся вопросу, вы хотите сохранить специальные значения указателя (1,2,3) для указания дополнительной информации.
НЕТ!! Не делайте этого!!. Нет никакой гарантии, что 1, 2 и 3 не являются правильными указателями. Это может иметь место в системах, где вам необходимо выровнять указатели по 4-байтовым границам, но, поскольку вы спрашивали обо всех архитектурах, я предполагаю, что переносимость у вас имеет большое значение.
Найдите другой способ сделать это правильно. Например, используйте союз (синтаксис по памяти, может быть неверный):
typedef struct {
int isPointer;
union {
int intVal;
void *ptrVal;
}
} myType;
Затем вы можете использовать логическое значение isPointer, чтобы решить, следует ли вам рассматривать объединение как целое число или указатель.
РЕДАКТИРОВАТЬ:
Если скорость выполнения имеет первостепенное значение, то решение typedef — это то, что вам нужно. По сути, вам нужно будет определить целое число, которое вы хотите для каждой платформы, на которой вы хотите работать. Вы можете сделать это с помощью условной компиляции. Я бы также добавил проверку во время выполнения, чтобы убедиться, что вы правильно скомпилировали для каждой платформы (я определяю это в исходном коде, но вы бы передали это как флаг компилятора, например «cc -DPTRINT_INT
»):
#include <stdio.h>
#define PTRINT_SHORT
#ifdef PTRINT_SHORT
typedef short ptrint;
#endif
#ifdef PTRINT_INT
typedef int ptrint;
#endif
#ifdef PTRINT_LONG
typedef long ptrint;
#endif
#ifdef PTRINT_LONGLONG
typedef long long ptrint;
#endif
int main(void) {
if (sizeof(ptrint) != sizeof(void*)) {
printf ("ERROR: ptrint doesn't match void* for this platform.\n");
printf (" sizeof(void* ) = %d\n", sizeof(void*));
printf (" sizeof(ptrint ) = %d\n", sizeof(ptrint));
printf (" =================\n");
printf (" sizeof(void* ) = %d\n", sizeof(void*));
printf (" sizeof(short ) = %d\n", sizeof(short));
printf (" sizeof(int ) = %d\n", sizeof(int));
printf (" sizeof(long ) = %d\n", sizeof(long));
printf (" sizeof(long long) = %d\n", sizeof(long long));
return 1;
}
/* rest of your code here */
return 0;
}
В моей системе (Ubuntu 8.04, 32-разрядная версия) я получаю:
ERROR: ptrint typedef doesn't match void* for this platform.
sizeof(void* ) = 4
sizeof(ptrint ) = 2
=================
sizeof(short ) = 2
sizeof(int ) = 4
sizeof(long ) = 4
sizeof(long long) = 8
В этом случае я бы знал, что мне нужно скомпилировать с PTRINT_INT (или long). Возможно, есть способ поймать это во время компиляции с помощью #if, но я не мог утруждаться его исследованием в данный момент. Если вы столкнетесь с платформой, где нет целочисленного типа, достаточного для хранения указателя, вам не повезло.
Имейте в виду, что использование специальных значений указателя (1,2,3) для представления целых чисел также может работать не на всех платформах — на самом деле это могут быть допустимые адреса памяти для указателей.
Тем не менее, если ты собираешься проигнорировать мой совет, я мало что могу сделать, чтобы остановить тебя. В конце концов, это ваш код :-). Одна из возможностей состоит в том, чтобы проверить все ваши возвращаемые значения из malloc и, если вы получите 1, 2 или 3, просто снова использовать malloc (т. е. иметь mymalloc(), который делает это автоматически). Это будет небольшая утечка памяти, но она гарантирует отсутствие конфликтов между вашими специальными указателями и реальными указателями.
#ifdef DEVMODE
, чтобы он не влиял на производственный код.
- person paxdiablo; 20.10.2012
Стандарт C99 определяет стандартные типы int:
7.18.1.4 Целочисленные типы, способные содержать указатели на объекты Следующий тип обозначает целочисленный тип со знаком, обладающий тем свойством, что любой действительный указатель на void может быть преобразован в этот тип, а затем преобразован обратно в указатель на void, и результат будет равен оригинальный указатель:
intptr_t
Следующий тип обозначает беззнаковый целочисленный тип со свойством, согласно которому любой допустимый указатель на void может быть преобразован в этот тип, а затем преобразован обратно в указатель на void, и результат будет равен исходному указателю:
uintptr_t
Эти типы являются необязательными.
C99 также определяет size_t и ptrdiff_t:
Типы
ptrdiff_t
который представляет собой целочисленный тип со знаком результата вычитания двух указателей;
size_t
который представляет собой беззнаковый целочисленный тип результата оператора sizeof; и
Архитектуры, которые я видел, имеют максимальный размер объекта, равный всей памяти, поэтому sizeof(size_t) == sizeof(void*), но я не знаю ничего, что было бы одновременно переносимо на C89 (который size_t
является ) и гарантированно будет достаточно большим (что uintptr_t
).
Это было бы верно для стандартной 32-битной системы, но, безусловно, нет никаких гарантий, и вы можете найти множество архитектур, где это не так. Например, распространенное заблуждение состоит в том, что sizeof(int) на x86_64 будет равен 8 (поскольку я думаю, что это 64-битная система), что не так. В x86_64 sizeof(int) по-прежнему равен 4, но sizeof(void*) равен 8.
Стандартным решением этой проблемы является написание небольшой программы, которая проверяет размеры всех типов int (short int, int, long int) и сравнивает их с void*. Если есть совпадение, он выдает фрагмент кода, определяющий тип intptr. Вы можете поместить это в заголовочный файл и использовать новый тип.
Этот код легко включить в процесс сборки (например, используя make
)
Нет, ближе всего к переносимому целочисленному типу с поддержкой указателя будут intptr_t
и ptrdiff_t
.
No.
Я не верю, что стандарт C даже определяет стандартные размеры целых чисел. Объедините это со всеми существующими архитектурами (8/16/32/64 бит и т. д.), и нет никакого способа гарантировать что-либо.
тип данных int был бы ответом на большинстве архитектур.
Но нет НИКАКИХ гарантий для ЛЮБОЙ (микро)архитектуры.
int
обычно составляет 32 бита на x86-64, поэтому в некоторых случаях вы можете предпочесть использовать long
.
- person Magnus Hoff; 02.02.2009
Ответ кажется «нет», но если все, что вам нужно, это тип, который может действовать как оба, вы можете использовать объединение:
union int_ptr_t {
int i;
void* p;
};
Обычно sizeof(*void) зависит от ширины шины памяти (хотя это и не обязательно — AS/400 до RISC имела 48-битную адресную шину, но 64-битные указатели), а int обычно равен размеру регистра общего назначения ЦП (есть также исключения - SGI C использовала 32-битные целые числа на 64-битных MIPS).
Так что гарантии нет.