Почему `uint32_t` typedeffed в` unsigned long` в GCC arm-none-eabi и как его изменить? - PullRequest
1 голос
/ 21 апреля 2019

Я использую arm-none-eabi-gcc 7.4 для компиляции "голой" программы для микроконтроллера на базе ARM Cortex-M4 (в частности, EFM32WG940, но это не имеет отношения к вопросу).

Насколько я вижу, sizeof(int) и sizeof(long int) вместе с их неподписанными аналогами равны 4. Вам действительно нужно набрать long long int, чтобы получить 64-разрядное целое число (или int64_t), поэтому long int кажется излишним.

Почему в этой среде предопределенный макрос __UINT32_TYPE__ предопределен и определен как long unsigned int?

Есть ли способ заменить uint32_t на unsigned int вместо?

Обычно это не беспокоит меня, но это заставляет printf доставить мне неприятности из-за -Wformat. Рассмотрим что-то вроде: printf("hello %u\n", i); (при условии i это uint32_t) что дает мне предупреждение, потому что %u ожидает unsigned, но uint32_t - unsigned long. Очевидно, что я могу изменить его на %lu, но тогда тот же код выдаст мне предупреждение при компиляции на x86.

EDIT:

Да, один из способов заставить замолчать -Wformat - это использовать макросы формата из inttypes.h, например: printf("hello %" PRIu32, i);, который на самом деле не отвечает на мой вопрос, будет вполне приемлемо, если код не был таким уродливым, и у некоторых статических анализаторов с ним не было проблем.

Вместо того, чтобы взламывать все мои вызовы printf, я хотел бы понять, почему uint32_t должно быть unsigned long int вместо unsigned int на этой платформе, и как это изменить.

1 Ответ

1 голос
/ 21 апреля 2019

Насколько я вижу, sizeof(int) и sizeof(long int) вместе с их беззнаковыми аналогами равны 4. Вам на самом деле нужно набрать long long int, чтобы получить 64-разрядное целое число (или int64_t),так что long int кажется совершенно лишним.

Язык Си не определяет точную ширину стандартных целочисленных типов.Скорее, он определяет минимальный диапазон значений, которые каждое из них должно представлять.Это позволяет писать С естественным образом для целей с различной архитектурой.В частности, соответствующая реализация может обеспечить int s всего 15 битами значения, тогда как long int должно иметь не менее 31, а long long int должно иметь не менее 63. Целочисленные типы со знаком также имеют знаковый бит,а беззнаковые аналоги - один дополнительный бит значения.(Любой из них может также иметь биты заполнения, хотя это редко.) Так что нет, long int не лишний, по крайней мере, не в семантическом смысле.

В этой среде почему __UINT32_TYPE__ предопределенный макрос, определенный как long unsigned int?

Во-первых, __UINT32_TYPE__ - это деталь реализации glibc.Таким образом, единственный разумный ответ на поставленный вопрос - «потому что unsigned long int - это тип, выбранный для uint32_t».Но я хочу, чтобы вы спросили, почему этот тип был выбран в первую очередь.Я не могу однозначно говорить о причинах решений разработчиков GCC / glibc здесь, но я думаю, что я бы сделал такой же выбор на их месте, где они пытаются поддерживать самые разные архитектуры.

Потому что long int и unsigned long int гарантированно имеют как минимум 31 и 32 значения битов соответственно (а int и unsigned int - нет), длинные версии являются естественным выбором для [u] int32_t на этих платформахгде они фактически соответствуют его требованиям ( в точности 32 бита, без дополнительных битов, а подписанная версия реализована как дополнение к двум).Длинные версии являются единственным правильным выбором для некоторых поддерживаемых архитектур, и они являются одним из приемлемых вариантов для большинства остальных.Выбирая длинные версии, glibc должен делать исключения только для подмножества 64-разрядных платформ и, возможно, для нескольких платформ с целочисленными типами странных чисел.

Нет никаких требований и никаких оправданий для любых ожиданий, чтоint и unsigned int будут типами, выбранными для int32_t и uint32_t, когда они удовлетворяют требованиям для этого, если только они не являются только доступными типами, которые удовлетворяют требованиям.

Есть ли способ заменить uint32_t на unsigned int вместо этого?

Не существует соответствующего способа для вашего кода C изменить определение uint32_t, предоставленноереализация.Вы можете попробовать взломать реализацию C, чтобы внести изменения, или вы можете выбрать совершенно другую реализацию, но любая попытка переопределить уже определенный тип приводит к неопределенному поведению (и, вероятно, но не обязательно, отклоняется компилятором).

Стандартный способ справиться с изменчивостью типов при использовании типов явной ширины с отформатированными функциями ввода-вывода - это использовать предоставленные для этой цели макросы , о которых вы уже знаете,В качестве альтернативы, если вы удовлетворены тем, что знаете применимые определения типов, используемых для всех интересующих платформ, вы можете написать соответствующие форматы напрямую.Или вы можете напрямую использовать стандартный тип по вашему выбору (, например, unsigned int) или, для printf, но не scanf, привести аргумент к типу, соответствующему формату.

Не пренебрегайте альтернативой использования стандартных типов вместо псевдонимов типов с явной шириной.Первые являются более плавной альтернативой, когда их достаточно - нет необходимости в каких-либо специальных заголовках и плавная совместимость со всеми стандартными библиотечными функциями.

Очевидно, я могу изменить его на %lu, но затем то же самоекод выдаст мне предупреждение при компиляции на x86.

Может быть, а может и нет. Тип unsigned long имеет ширину 32 бита на glibc и для 32-битной x86. Я не уверен, что это тип, выбранный для int32_t, но это одна из жизнеспособных альтернатив. То же самое верно даже для некоторых 64-битных платформ, таких как Win64. Для любой данной платформы, где обе альтернативы жизнеспособны, вы, вероятно, найдете несколько реализаций, которые выбирают одну, и некоторые, которые выбирают другую.

...