C размеры типов данных intmax_t по сравнению с любым другим целым числом, void * по сравнению с любым другим указателем

#c #sizeof

Вопрос:

Правда ли, что:

  • Нет указателя на данные больше, чем sizeof(void *) ?
  • Нет указателя на данные больше, чем sizeof(char *) ?
  • Никакое целое число не больше, чем sizeof(intmax_t) ?
  • Ни float то, ни double другое не больше, чем sizeof(long double) ?

Я знаю, что intmax_t может хранить любое значение, которое может хранить любое другое целое число со знаком, но требуется ли, чтобы размер также был наибольшим размером любого целого числа? Или возможно, что какое-то другое целое число использует так много битов заполнения intmax_t , что размер этого целого числа становится больше, чем intmax_t даже тогда, когда это целое число не может содержать какое-либо значение, которое intmax_t не может содержать?

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

Я задаю этот вопрос, потому что у меня есть функция в библиотеке, которая преобразует строку в другой тип, который обозначается целым числом. Чтобы проверить, можно ли выполнить этот диалог для данной строки (формат правильный), существует функция, которая проверяет это для разных типов данных, функция должна зарезервировать достаточно памяти для всех возможных типов, выполнить диалог, проверить наличие ошибок и снова освободить эту память. Достаточно ли этого, чтобы убедиться, что он достаточно велик intmax_t , чтобы охватить все целочисленные типы?

Комментарии:

1. Стандарт C не требует этого. Реализация может, скажем, включать метаданные , с int * которыми она не имеет void * , делая int * их больше void * , даже если она не может охватить столько различных местоположений. Или он мог бы просто сказать «трахнись и float набери сотню неиспользуемых байтов». Или, может быть, a void * составляет всего восемь байт для доступа к любому 64-разрядному адресу, но все указатели функций составляют 16 байт, потому что они содержат некоторую информацию о безопасности.

2. @EricPostpischil К указателям на функции: В C нет требований, которые void * могли бы содержать указатель на функцию, на самом деле на некоторых микроконтроллерах с гарвардской архитектурой указатель на функцию больше, чем void * . В остальном: это относится к ответу.

3. Короткая нетехническая версия представляет собой указатель-это указатель-это указатель (и для любой данной реализации существует только один размер). stdint.h Тип intmax_t может содержать любое целое значение со знаком, и без учета нечетных 12-битных чисел с плавающей запятой или точности, превышающей double и т. Д. Тогда, как правило, можно с уверенностью предположить, что ни float то, ни другое или double больше long double . Но, как уже отмечалось, стандарт конкретно не отвечает на ваш вопрос, поэтому он будет зависеть от конкретной реализации (хотя я не знаю ни одного, который нарушал бы ваши утверждения).

4. Существует причина, по которой стандарт C не требует, чтобы «указатель-это указатель-это указатель (и для любой данной реализации существует только один размер)». Причина в том, что это просто неверно для нескольких (ранее) важных архитектур. c-faq.com/null/machexamp.html

5. «Ни одно целое число не больше, чем sizeof(intmax_t)» — sizeof(intmax_t) обычно равно 4 или 8.

Ответ №1:

Есть intptr_t сейчас, которое гарантированно работает; однако платформы прошлых лет, на которых intptr_t нужно было бы быть больше, чем ptrdiff_t нет intptr_t , потому что они заморожены во времени, а собственные компиляторы для целевых платформ просто не имеют этого. Если у вас есть современный компилятор, такой как OpenWatcom, ориентированный на старые архитектуры, он будет работать.

Хотя они исправили это для кросс-компиляции современных компиляторов во встроенные процессоры, вы в конечном итоге получаете другие аберрации, которые могут существовать вместо этого, которые так же плохи и одинаково трудны для проверки. У компиляторов, с которыми мне приходилось иметь дело во встроенном мире, были некоторые реальные неприятности; если они типичны, попытка создать нейтральную для платформы библиотеку, которая не предполагает плоской архитектуры, чревата перлом:

  • Значение NULL не равно 0. Это означает, что memset() это не инициализирует указатели в структурах на NULL и не делает calloc() этого ; и они не являются нулевыми, когда объявлены как статические, если явно не инициализированы на NULL .
  • sizeof(char *) < sizeof(const char *) и sizeof(void *) < sizeof(const void *) ; этот конкретный компилятор не давал intptr_t и ptrdiff_t не мог содержать результат произвольного вычитания двух указателей в одном и том же массиве символов (но этого можно было избежать, сделав массив символов не больше PTRDIFF_T_MAX).
  • Компилятор не реализовал C99 и не имел intptr_t и ptrdiff_t был слишком мал, чтобы содержать указатель.
  • free() ничего не сделал
  • Компилятор статически удалил тесты с нулевым указателем, где он мог доказать, что указатель указывает на структуру, но NULL был возможным адресом глобальной переменной.

Я никогда не пытался заставить нетривиальную библиотеку работать во встроенном мире и, конечно, никогда не сталкивался с такими указателями, как этот.

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

Мне приходит в голову, что вы конвертируете между указателем и строкой sprintf и sscanf у вас есть спецификаторы формата, которые могут это сделать.

Комментарии:

1. Первое и последнее предложение неверно.

2. где intptr_t должно быть больше, чем ptrdiff_t нет intptr_t , я не понимаю, почему это должно быть так, или вот что: если вы не можете привести указатель в ptrdiff_t безопасное intmax_t положение, его вообще не будет , можете ли вы объяснить, почему это должно быть так? AFAIK: Эти утверждения неверны для некоторых компиляторов 8051.

3. OpenWatcom для 8086, использующий модель большой памяти, является примером компилятора/библиотеки , которая использует 32-разрядный код для intptr_t , но ptrdiff_t имеет только 16-разрядный код и intmax_t существует. Является ли это несоответствием? github.com/jmalak/open-watcom/blob/master/bld/hdr/watcom/…

4. @12431234123412341234123: Большое обновление для ответа; Я добавил много материала из встроенного мира. Я удивлен, что кто-то поддерживает компилятор x86-16 в соответствии с современными стандартами, но это так, поэтому я включил его в ответ.