Чи неналежний long long 64-бітЧи неналежний long long 64-біт

0 Comment

C: long long always 64 bit?

If i’m using long longs in my code, can i absolutely 100% guarantee that they will have 64 bits no matter what machine the code is run on?

4 Answers 4

No, C99 standard says that it will have at least 64 bits. So it could be more than that at some point I guess. You could use int64_t type if you need 64bits always assuming you have stdint.h available (standard in C99).

int64_t must be provided if the implementation has a type that fits its requirements — but it’s (at least theoretically) possible that no such type exists, in which case int64_t won’t be defined.

You can test if your compiler is C99 complying with respect to numbers in the preprocessor with this

This works since all unsigned values (in the preprocessor) are required to be the same type as uintmax_t and so 0U is of type uintmax_t and ~0U , 0U-1 and -1U all are the maximum representable number.

If this test works, chances are high that unsigned long long is in fact uintmax_t .

For a valid expression after the preprocessing phase to test this with the real types do

unsigned long long has_ullong_max[-1 + 2*((0ULL - 1) >= 18446744073709551615ULL)]; 

This does the same sort of trick but uses the postfix ULL to be sure to have constants of type unsigned long long .

Зачем нужен тип long когда есть int?

В С есть тип long . Согласно: Wiki c-types он занимет в памяти 4 байта, да и диапазон значений у него такой же как и у int . Тогда зачем он нужен? Выписка из wiki: int :

Основной тип целого числа со знаком. Может содержать числа в диапазоне [−32767, +32767]. range;[3][4] Таким образом, это по крайней мере 16 бит (2 байта). На практике, во всех современных компиляторах имеет размер в 4 байта и диапазон [-2147483648, +2147483647]

Тип длинного целого числа со знаком. Может содержать числа, как минимум, в диапазоне [−2 147 483 647, +2 147 483 647].[3][4] Таким образом, это по крайней мере 32 бита (4 байта).

На практике long чаще всего вдвое больше int. Стандарт гарантирует, что не меньше, но не запрещает быть больше.

@PavelMayorov Никто не будет против, если вы дополните мой комментарий своими примерами. К чему же этот флуд?

@free_ze, очевидно, что на распространенных пользовательских платформах, которые средний по больнице Вася встречает чаще всего — не, не на распространенных пользовательских, а на правильных (а наиболее распространенная винда к ним не относится)

4 ответа 4

Размеры типов int и long не регламентированы Стандартом языка. Но регламентировано отношение их размеров, т.е. sizeof(int)

На текущий момент можно сказать, что размер long зависит как от разрядности процессора, так и от используемой ОС. Например, для Linux и MacOS он будет 8 байт для 64bit архитектур, и 4 байта для 32bit архитектур. В Windows размер будет 4 байта, независимо от разрядности архитектуры.

Например, в Стандарте С++ даже встречается такой пример:

static_assert(sizeof(long) >= 8, "64-bit code generation required for this library."); 

Использовать разные типы можно, например, для обеспечения перегрузки функций:

void f(int) <> void f(long) <> f(42); // int f(42L); // long 

Также не стоит забывать, что данные типы имеют разный ранг (integer conversion rank), что влияет на правила преобразования целочисленных типов.

Уточнение по поводу размеров long после комментариев @AnT со страницы cygwin:

While the Mingw and Microsoft compilers use the LLP64 data model, Cygwin compilers use the LP64 data model, just like Linux. This affects the size of the type long. In the LLP64 model preferred by Microsoft, sizeof(long) is 4. This applies for the related Win32 types like LONG, ULONG, DWORD, etc., too.

In the LP64 model used by Cygwin, sizeof(long) is 8, just like the size of pointers or the types size_t/ssize_t. This simplifies porting Linux applications to 64 bit Cygwin, but it requires due diligence when calling Windows functions taking LONG, ULONG, DWORD, or any other equivalent type. This is especially important in conjunction with pointers.

Ответ вводит в заблуждение. Размер целого С/С++ типа не зависит от используемой ОС вообще никак. Размер типа зависит только от компилятора и больше ни от чего. Компилятор, разумеется, в вопросах выбора размера типа будет руководствоваться сображениями эффективности, что привнесет сюда и учтение разрядности процессора. Так что разрядность процессора косвенно/опосредованно влияет. Точнее, не “влияет”, а скажем, “снисходительно/необязательно учитывается” при принятии таких решений. Кроме компилятора на размер типа не влияет больше ничто.

@AnT мы же не о сферических конях в вакууме тут беседуем. Да и не сам я это придумал, и даже ссылку добавил. Плюс пример из вполне настоящего Стандарта.

При чем здесь сферические кони? Нет никаких причин типу long быть 4 байта “на Windows”. Более того, это утверждение соврешено не верно: в компиляторе MSVC на Windows long – 4 байта, в компиляторе GCC на Windows long – 8 байт. Так что Windows тут никакой роли не играет. Это вопрос решения авторов компилятора, котрое зависит от соврешенно иных причин. Да и тот факт, что в 64-битном мире тип int не стал 64-битным (как рекомендуют стандарты С и С++) практически ни в одном компиляторе говорит о том, что вопросы обратной совместимости играют бОльшую роль, чем разрядность процессора.

@AnT “в компиляторе GCC на Windows long – 8 байт.” Я вот поставил MinGW-w64 и тестовый код говорит, что sizeof(long) == 4 для 64bit окружения. ОС Win7.

А я вот поставил Cygwin и тестовый код говорит, что sizeof(long) == 8 . Ваш MinGW, очевидно, эмулирует поведение MSVC для совместимости с его билиотеками.

Процессоры развивались постепенно все более расширяя емкость регистров. Например, ранее в DOS тип int соответствовал 16 разрядному целочисленному значению, так как на IBM-совместимых компьютеров регистры были 16-разрядными. Например, регистр AX является 16 разрядным, а его подрегистры AL и AH имели разрядность равную 8 битам. До этого вообще регистры процессоров были 8 разрядными.

Если выполнить, допустим, умножение двух объектов 16-разрядного типа int , то для хранения результата нужно использовать два регистра как, например, регистры AX и DX . Для этого результата уже нужно вводить новый целочисленный тип. И такой тип был введен. Это тип long .

Затем появились 64-разрядные процессоры. Необходимо различать 16-разрядные целые числа, 32-разрядные целые числа и 64- разрядные целые числа. Поэтому был введен дополнительный целый тип Long long . Имейте в виду, что нужно было сохранять обратную совместимость с целыми типами, введенными ранее для процессоров с меньшей разрядностью.

Поэтому в стандарте C принято, что

У разработчиков компиляторов есть свобода выбора, какая разрядность будет у типа long и других целочисленных типов. Например, разрядность типа long может составлять не `32 бита, а 64 бита.

Чтобы программы были переносимы, возникла необходимость вводить новые типы со строго указанной разрядностью такие, как, например, int_least32_t или int32_t.

На одних 64-битовых платформах тип long и тип long long могут иметь 64-битовуж разрядность. На других 64-битовых платформах тип long может быть 32-битовым и тип int также может быть 32-битовым, а тип long long – 64 битовым.

Тем не менее ранг типа long long больше ранга типа long , а тип long в свою очередь имеет ранг выше, чем тип int .

На этом основываются правила преобразования типов в выражениях. Например, если тип long и тип int имеют разрядность равную 32 битам, то в следующем фрагменте кода

long x = 0; unsigned int y = 0; x + y; 

тип выражения x + y имеет тип unsigned long .

В связи с этим имеются некоторые курьезы, связанные с такими преобразованиями особенно при выборе перегруженной функции в C++.

Один из таких примеров описан в следующей теме, где ближе к ее концу (можно быстро найти это сообщение по ключевому слову int64_t ) описывается один из таких курьезов, связанных с вызовом перегруженной функции, у которой параметр имеет тип std::int64_t , который, как оказалось, является не алиасом типа long long int , а алиасом типа long int , который на данной платформе является 64-битовым.