Длинный длинный в c99
В стандарте C99 они ввели long long
. Какая у этого цель? В моем (ограниченном) опыте программирования на C я видел только 4-байтовые int и 8-байтовые. Например, из Compiler Explorer:

Если long
уже, 8
то зачем добавлять другой long long
тип? Что это делает с компилятором / архитектурой?
Ответы
Если long уже 8, то зачем добавлять еще один длинный длинный тип? Что это делает с компилятором / архитектурой?
«Если long уже 8» не всегда верно, так как существует много кода, основанного на 32-битном коде, long
а int
также на 32- или 16-битном.
Требование long
64-битной версии нарушило бы кодовую базу. Это серьезная проблема.
Тем не менее, требование long
оставаться 32-битным (и нет long long
) не приведет к доступу к стандартным 64-битным целым числам, следовательно, обоснование для long long
.
Разрешение long
32-битной или 64-битной (или других) разрешает переход.
Различные функции передаются / возвращаются long
вроде fseek(), ftell()
. Они выигрывают от long
того, что они более 32-битные для поддержки больших файлов.
Рекомендуемая практика поощряет более широкое long
: «Типы, используемые для size_t
и ptrdiff_t
не должны иметь ранг целочисленного преобразования выше, чем у, signed long int
если реализация не поддерживает объекты, достаточно большие, чтобы сделать это необходимым». Это относится к объемам памяти, превышающим 32 бита.
Возможно, в будущем реализация может использовать int/long/long long/intmax_t
как 32/64/128/256 бит.
IAC, я вижу, что типы фиксированной ширины intN_t
становятся все более популярными по сравнению с long
и long long
. Я обычно использую типы фиксированной ширины or bool
, ( unsigned
) char
, int
/ unsigned
,, size_t
( u
) intmax_t
и leave signed char
, ( unsigned
) short
, ( unsigned
) long
, ( unsigned
) long long
для особых случаев.
Стандарт C гарантирует только то, что an int
может быть (грубо говоря) 2 байтами, a long
может быть 4 байтами, а a long long
может быть 8 байтами.
Фактически, MSVC по-прежнему использует 4 байта, long
хотя имеет 4 байта int
.
Единственное соответствующее требование для int
и long
тогда и сейчас - это int
должно быть не менее 16 бит и long
должно быть не менее 32 бит. И 16-, и 32-битные системы обычно имеют 32-битные long
, а 64-битные машины были гораздо менее распространены в конце 1990-х годов. Таким образом, до C99 программисты вообще не могли полагаться на доступный 64-битный целочисленный тип. Эта проблема была решена введением long long
, которое должно быть не менее 64 бит. (Я считаю, что он уже был предоставлен GCC и, возможно, другими компиляторами в качестве расширения).
В наши дни многие (но не все) 64-битные системы используют 64-битные long
и не пытаются long long
увеличивать их, поэтому они также 64-битные и в некотором смысле избыточны. Предположительно, это системы, с которыми вы знакомы, но они не отражают все, что есть.
Я думаю, вы не осознавали, что делаете огромное неверное предположение о том, как работают требования к ширине типа C: ISO C просто устанавливает минимальный диапазон значений, например, наименьшее допустимое значение LONG_MAX
и LONG_MIN
(-2147483647, а не 8, потому что ISO C позволяет дополнять целые числа со знаком / величиной со знаком, а не только дополнять до 2). Фактические реализации могут иметь более широкие типы, часто для соответствия ширине регистра или размеру операнда, которые целевая машина может эффективно выполнять.
Об этом много написано в Stack Overflow и в других местах, и я не собираюсь повторять здесь. Смотрите такжеhttps://en.cppreference.com/w/c/language/arithmetic_types
Это привело вас к ошибке, когда вы смотрели на выбор ширины шрифта в x86-64 System V ABI и предполагали, что другие реализации C такие же, я думаю. x86-64 - это 64-битный ISA, который может эффективно работать с 64-битными целыми числами, поэтому 64-битная версия long
была вполне разумным выбором.
Ни один нормальный ABI для 32-битной машины, такой как i386, не будет использовать 64-битную, long
потому что это не требуется, только 32-битная. Использование 64-битной версии означало бы, что она не могла поместиться в один регистр. Скомпилируйте -m32
или скомпилируйте для 32-битной ARM. Godbolt также имеет GCC для AVR и MSP430. На этих 8-битных и 16-битных машинах GCC выбирает наименьшую ширину, разрешенную ISO C (2 байта int
и т. Д.).
В 1999 году x86-64 даже не существовало. (Несколько других 64-битных ISA сделали, например Alpha). Так что, глядя на один из двух основных ABI, чтобы понять, какие варианты выбора C99, вы далеко не уедете.
Конечно, C нужен тип, который гарантированно будет как минимум 64-битным, чтобы люди могли писать программы, которые эффективно выполняют 64-битную целочисленную математику.
И, кстати, x86-64 может делать 32-битные целые числа так же эффективно, как 64-битные, иногда более эффективно. Так что long
создание 64-битного типа, пожалуй, не очень хорошо. Некоторый код использует, long
потому что им нужен тип, который должен быть 32-битным, но не выигрывает от его более широкого. Для такого кода 64-разрядная long
версия просто тратит впустую объем кеш-памяти / пропускную способность памяти и размер кода (префиксы REX). В C99 был бы идеальный выбор int_least32_t
, но это раздражающе долго печатать и редко используется.
Но long
иногда надеются, что OTOH будет «наиболее эффективным (с 1 регистром) типом», хотя такой гарантии нет, и ABI LLP64, такие как Windows x64 с 32-разрядной версией long
, не такие.
Еще одна целая банка червей - int_fast32_t
это плохой выбор IMO для C99 и x86-64 System V, чтобы сделать этот тип 64-битным. (У меня есть наполовину написанный ответ для Cpp uint32_fast_t разрешается в uint64_t, но медленнее почти для всех операций, чем uint32_t (x86_64). Почему он разрешается в uint64_t? Который я должен закончить ... int_fast32_t
поднимает вопрос «быстро для чего цель ", и во многих реализациях это не то, на что можно надеяться во многих случаях.
Смотрите также
- C ++ - самый быстрый целочисленный тип?
- Как следует определять типы [u] int_fastN_t для x86_64, с ABI для x32 или без него?
- Почему uint32_t предпочтительнее uint_fast32_t?
- Почему uint_least16_t быстрее, чем uint_fast16_t для умножения в x86_64?
- Оптимизация компилятора разрешена с помощью типов "int", "минимум" и "fast" с нефиксированной шириной C / C ++
Существуют некоторые ограничения, но автор компилятора может выбирать длины для стандартных типов переменных C (char, short, int, long, long long). Естественно, что char будет байтом для этой архитектуры (большинство компиляторов C имеют 8 бит). И, естественно, что-то меньшее не может быть больше, чем что-то большее, long не может быть меньше int. Но, безусловно, к 1999 году мы увидели переход x86 с 16 на 32 бит, и, например, int изменился с 16 на 32 с помощью ряда инструментов, но долгое время оставался 32. Позже произошел переход с 32 на 64 бит x86, и в зависимости от инструмента были доступны типы помогать.
Проблема существовала задолго до этого, и решение состояло не в том, чтобы фиксировать длину типов, они, в пределах правил, устанавливаются авторами компилятора в отношении размера. Но авторам компилятора необходимо создать файл stdint.h, который соответствует инструменту и цели (stdint.h как минимум специфичен для инструмента и цели и может быть версией инструмента и опциями сборки для этого инструмента и т. Д.). Так что, например, uint32_t всегда 32 бита. Некоторые авторы преобразуют это в int, другие в long и т. Д. В своем stdint.h. Типы переменных языка C по-прежнему ограничены char, short, int и т. Д. Для каждого языка (uint32_t не является типом переменной, он преобразуется в тип переменной через stdint.h). Это решение / обходной путь был способом уберечь всех от сумасшествия и сохранить язык.
Авторы часто выбирают, например, если GPR 16-битные, чтобы int было 16-битным, а если 32-битное было 32-битным и так далее, но у них есть некоторая свобода.
Да, это конкретно означает, что нет причин предполагать, что любые два инструмента для конкретной цели (например, компьютер, на котором вы читаете это) используют одни и те же определения для int и long, в частности, и если вы хотите написать код для эта платформа, которая может переносить эти инструменты (которые поддерживают эту платформу), затем используйте типы stdint.h, а не int, long и т.д ... Скорее всего, если вы пересекаете платформы, msp430 mcu, arm mcu, arm linux машина , машина на базе x86, типы которой, даже для одной и той же "цепочки инструментов" (например, gnu gcc и binutils), не имеют одинаковых определений для int и long и т. д. char и short, как правило, 8- и 16-битные, int и long имеют тенденцию сильно различаться, иногда одного и того же размера, иногда разного, но дело не в том, чтобы предполагать.
Определить размеры для версии компилятора / цели / параметров командной строки - тривиально или просто перейти по маршруту stdint, чтобы впоследствии минимизировать проблемы.