sizeof (int) == sizeof (void *)? - PullRequest
       18

sizeof (int) == sizeof (void *)?

13 голосов
/ 02 февраля 2009

Есть ли целочисленный тип с таким же размером, что и указатель? Гарантируется на всех микроархитектурах?

Ответы [ 11 ]

16 голосов
/ 02 февраля 2009

Согласно этой странице Википедии , в C99 ваш заголовок stdint.h может объявить intptr_t и uintptr_t, но тогда это, конечно, требует

  • C99
  • Разработчик компилятора, который решил реализовать эту необязательную часть стандарта

Так что в целом я думаю, что это сложно.

14 голосов
/ 02 февраля 2009

Проще говоря, нет. Не гарантируется для всех архитектур.

Мой вопрос: почему? Если вы хотите выделить достаточно большой тип для хранения void*, лучше всего выделить (как ни удивительно :-) a void*. Почему необходимо установить его в пределах int?

РЕДАКТИРОВАТЬ: Исходя из ваших комментариев к дублирующемуся вопросу, вы хотите сохранить специальные значения указателя (1,2,3) для указания дополнительной информации.

NO !! Не делай этого !! . Нет никакой гарантии, что 1, 2 и 3 не являются абсолютно правильными указателями. Это может иметь место в системах, где требуется выравнивание указателей на 4-байтовых границах, но, поскольку вы спросили обо всех архитектурах, я предполагаю, что у вас высокая переносимость.

Найдите другой способ сделать это правильно. Например, используйте объединение (синтаксис из памяти может быть неправильным):

typedef struct {
    int isPointer;
    union {
        int intVal;
        void *ptrVal;
    }
} myType;

Затем вы можете использовать isPointer 'boolean', чтобы решить, следует ли вам рассматривать объединение как целое число или указатель.

EDIT:

Если скорость выполнения имеет первостепенное значение, то решение 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 (), которая делает это автоматически). Это будет небольшая утечка памяти, но она не будет конфликтовать между вашими специальными указателями и реальными указателями.

13 голосов
/ 02 февраля 2009

Стандарт 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 является).

4 голосов
/ 02 февраля 2009

Это было бы верно для стандартной 32-битной системы, но, конечно, нет никаких гарантий, и вы можете найти множество архитектур, где это не так. Например, распространенное заблуждение состоит в том, что sizeof (int) в x86_64 будет 8 (так как это 64-битная система, я думаю), а это не так. На x86_64 sizeof (int) по-прежнему равен 4, а sizeof (void *) равен 8.

3 голосов
/ 02 февраля 2009

Так что насчёт C ++. Должен ли я идти на длины:

template <int BITS> struct int_traits_t;
template <> struct int_traits_t <16> {typedef uint16_t int_t;};
template <> struct int_traits_t <32> {typedef uint32_t int_t;};
template <> struct int_traits_t <64> {typedef uint64_t int_t;};
typedef int_traits_t <sizeof (void*) * 8>::int_t myintptr_t;

просто чтобы получить переносимое целое число размера указателя?

2 голосов
/ 02 февраля 2009

Стандартное решение этой проблемы - написать небольшую программу, которая проверяет размеры всех типов int (short int, int, long int) и сравнивает их с void *. Если есть совпадение, он генерирует фрагмент кода, который определяет тип intptr. Вы можете поместить это в заголовочный файл и использовать новый тип.

Этот код просто включить в процесс сборки (например, используя make)

1 голос
/ 02 февраля 2009

Нет, ближе всего к переносимому целочисленному типу с поддержкой указателей будет intptr_t и ptrdiff_t

0 голосов
/ 02 февраля 2009

Обычно sizeof (* void) зависит от ширины шины памяти (хотя не обязательно - в pre-RISC AS / 400 была 48-битная адресная шина, но 64-битные указатели), а int обычно такой же большой, как регистр общего назначения CPU (есть и исключения - SGI C использовал 32-битные целые на 64-битных MIPS).

Так что нет гарантии.

0 голосов
/ 02 февраля 2009

Ответ кажется «нет», но если все, что вам нужно, это тип, который может действовать как оба, вы можете использовать объединение:

union int_ptr_t {
    int i;
    void* p;
};
0 голосов
/ 02 февраля 2009

тип данных int будет ответом на большинство архитектур.

Но есть НЕТ гарантия для ЛЮБОЙ (микро) архитектуры.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...