Long long in c99
Asked Answered
F

5

8

In the C99 standard they introduced long long. What is the purpose of this? In my (limited) C programming experience, I've only every seen a 4-byte int and an 8-byte long. For example, from Compiler Explorer: enter image description here

If long is already 8 then, why is it necessary to add another long long type? What does this do to the compiler/architecture?

Facetious answered 9/1, 2021 at 22:20 Comment(16)
8-byte long was not the norm in those days; 4-byte long was more common on 16- and 32-bit platforms (and still is). Of course 64-bit machines were rare in 1999.Longsufferance
long is 4 bytes in Visual C and 8 in gcc and for that reason I never use it.Erose
@WeatherVane what would you use instead of long then? int (doesn't that depend on the architecture too?)Facetious
@Facetious well, long long clearly.Vesture
@Facetious they depend on the implementation, within the limits set by the C standard. I often use the fixed size types int64_t etc, although the scanf() and printf() formatting specifiers are awkward and a pain to remember.Erose
@WeatherVane It is down to the platform, not the compiler. On Windows a long is 32bit even in gcc (assuming the native MinGW rather then Cygwin version that is). en.wikipedia.org/wiki/64-bit_computing#64-bit_data_modelsTiddly
There were many who objected to the introduction of long long in 1990s with the argument that it is redundant (as well as ugly, breaking compatibility, etc.).Fan
@Fan How does introducing long long break compatibility given code bases up to then did not have long long? Not being the widest integer type? I certainly see how requiring long as 64-bit would break things.Pun
@chux, In c89, long was guaranteed to be the longest integer type and there was code that relied on that guarantee. By allowing integer types larger than long, c99 could silently break that code, which was specifically against the committee guidelines.Fan
@Fredrik, that’s bad advice. For most variables in most portable code, you don’t need to know or enforce the exact sizes.Fan
@chux, Requiring long to be 64 bits (on systems that support 64 bit types) would only break code that relies on something not guaranteed by the standard (the size of long). The introduction of long long could break code that was specifically written to be as robust as possible by strictly following what was guaranteed by the standard (c89).Fan
@Fredrik and then you'll break your code while compiling for a 24-bit DSP for example, or have far inefficient code to do 32-bit loop counter for a 16-bit CPUDaliladalis
@Fan "Requiring long to be 64 bits ... would only break code that relies on something not guaranteed by the standard" is true, Still, much code would break because it was not written to account for that. There is not a solution that solves all issues - more of a comprise one was selected. In hindsight, we have the advantages not available to the C99 committee.Pun
@chux, yes, that was the huge argument at the time—do we value code that doesn’t follow the standard as much as code that does? And then, do we end up prioritizing it higher because there is so much more of it?Fan
@Fan Of course compliant code is more valuable. Yet C reflects community compromises versus single company edicts. I see non-compliant code is considered in language growth. Such code is not the priority, yet is is not ignored either.Pun
@Fan The mistake was not the C99 introduction of long long, it was the C89 guarantee tyuarantee that a 64-bit integer type would be available, and by the late 1990s it was blindingly obvious that 64-bit integer types were necessary and their existence must be guaranteed. Borrowing from the term "future-proof", that C89 guarantee "future-precluded" C and needed to be tossed.Lw
P
6

If long is already 8 then, why is it necessary to add another long long type? What does this do to the compiler/architecture?

"If long is already 8" is not always true as much code exists that relies on 32-bit long and int as 32 or 16 bits.

Requiring long as 64-bit would break code bases. This is a major concern.


Yet requiring long to remain 32-bit (and no long long) would not make for access to standard 64-bit integers, hence a rationale for long long.

Allowing long as either 32-bit or 64-bit (or others) allows for transition.

Various functions pass in/return long like fseek(), ftell(). They benefit from long being more than 32-bit for large file support.

Recommended practice encourages a wider long: "The types used for size_t and ptrdiff_t should not have an integer conversion rank greater than that of signed long int unless the implementation supports objects large enough to make this necessary." This relates to memory sizes exceeding 32-bit.


Perhaps in the future an implementation may use int/long/long long/intmax_t as 32/64/128/256 bits.

IAC, I see fixed width types intN_t increasing in popularity over long and long long. I tend to use fixed width types or bool, (unsigned) char, int/unsigned, size_t, (u)intmax_t and leave signed char, (unsigned) short, (unsigned) long, (unsigned) long long for special cases.

Pun answered 9/1, 2021 at 22:36 Comment(3)
do any programs still run on 16-bit machines today?Facetious
@Facetious Oh yes. Billions of embedded processes are made each year. A large share are 8/16-bit typically compiled with 16-bit int.Pun
@Facetious even 8-bit microcontrollers are still wildly common. They exist in lost of places where you don't realize like in your refrigerators, microwaves, speakers, home controllers, clocks, mice, keyboards, remote controls... There are even "odd" MCUs like 20, 24 or 28 or 48-bit ones for audio processingDaliladalis
E
4

The C standard only guarantees that an int can be (loosely speaking) 2 bytes, a long can be 4 bytes, and a long long can be 8 bytes.

In fact, MSVC still uses a 4 byte long even though it has a 4 byte int.

Eva answered 9/1, 2021 at 22:26 Comment(4)
long doesn't seem to have a place – you could have said "gcc uses an 8-byte long even though it has long long".Erose
@WeatherVane No, you can't say that. Because it has nothing to do with gcc. The size of a long on Linux is specified by the ABI, not gcc. On Windows, the size of a long in 64-bit executables is 4 bytes whether the compiler is gcc or MSVC.Lw
@AndrewHenle thanks - but my point still applies, with a little rephrasing.Erose
It is not MSVC++ specific, that is the WIn64 data model en.wikipedia.org/wiki/64-bit_computing#64-bit_data_modelsTiddly
L
3

The only relevant requirement for int and long, then and now, is that int must be at least 16 bits and long must be at least 32 bits. 16- and 32-bit systems both tend to have 32-bit long, and 64-bit machines were much less common in the late 1990s. So prior to C99, programmers could not portably rely on having a 64-bit integer type available at all. That problem was solved by the introduction of long long, which is required to be at least 64 bits. (I believe it was already provided by GCC and maybe other compilers as an extension).

These days, many (but not all) 64-bit systems use a 64-bit long and do not bother to make long long any bigger, so it is 64 bits as well and is in some sense redundant. Those are presumably the systems with which you're familiar, but they do not represent everything out there.

Longsufferance answered 9/1, 2021 at 22:38 Comment(0)
S
2

I think you didn't realize that you're making a huge wrong assumption about how C type-width requirements work: ISO C just sets a minimum value-range like the smallest-magnitude allowed LONG_MAX and LONG_MIN (-2147483647, not 8 because ISO C allows one's complement and sign/magnitude signed integers, not only 2's complement.) Actual implementations are allowed to have wider types, often to match a register width or operand-size the target machine can do efficiently.

Much has been written about this on Stack Overflow and elsewhere, which I'm not going to try to repeat here. See also https://en.cppreference.com/w/c/language/arithmetic_types


That led you to the mistake of looking at the type-width choices in the x86-64 System V ABI and assuming that other C implementations are the same, I think. x86-64 is a 64-bit ISA that can efficiently work with 64-bit integers, so 64-bit long was a fairly sane choice.

No sane ABI for a 32-bit machine like i386 would use 64-bit long because that's not required, only 32-bit. Using 64-bit would mean it couldn't fit into a single register. Compile with -m32, or compile for 32-bit ARM. Godbolt also has GCC for AVR and MSP430. On those 8-bit and 16-bit machines, GCC picks the smallest widths allowed by ISO C (2-byte int, etc.)

In 1999, x86-64 didn't even exist. (A few other 64-bit ISAs did, like Alpha). So looking at one of the 2 mainstream ABIs for it to understand C99 choices is not going to get you very far.

Of course C needs a type that's guaranteed to be at least 64-bit, to let people write programs that efficiently do 64-bit integer math.


And BTW, x86-64 can do 32-bit integer stuff as efficiently as 64-bit, sometimes more efficiently. So making long a 64-bit type is arguably not great. Some code uses long because they want a type that needs to be 32-bit, but doesn't benefit from having it wider. For such code, 64-bit long just wastes cache footprint / memory bandwidth, and code size (REX prefixes). In C99 the ideal choice would be int_least32_t, but that's annoyingly long to type and rarely used.

But OTOH, long is sometimes hoped to be "the widest efficient (1-register) type", although there's no such guarantee and LLP64 ABIs like Windows x64 with 32-bit long aren't like that.

Another whole can of worms is C99 int_fast32_t and x86-64 System V's IMO poor choice to make that a 64-bit type. (I have a half-written answer for Cpp uint32_fast_t resolves to uint64_t but is slower for nearly all operations than a uint32_t (x86_64). Why does it resolve to uint64_t? which I should finish... int_fast32_t raises the question of "fast for what purpose", and on many implementations it's not what you'd hope for many cases.

See also

Spectral answered 10/1, 2021 at 3:29 Comment(0)
C
0

There are some limits but the compiler author is free to choose the lengths for the standard C variable types (char, short, int, long, long long). Naturally char is going to be a byte for that architecture (most with C compilers are 8 bits). And naturally you cannot have something smaller be bigger than something bigger, long cannot be smaller than an int. But certainly by 1999 we saw the x86 16 to 32 bit transition and for example int changed from 16 to 32 with a number of tools but long stayed 32. Later the 32 to 64 bit x86 transition happened and depending on the tool there were types available to help.

The problem existed long before this and the solution was not to fix the lengths of the types, they are, within rules, up to the compiler authors as to size. But the compiler authors need to craft a stdint.h file that matches the tool and target (stdint.h is specific to a tool and target at a minimum and can be version of tool and build options for that tool, etc). So that, for example, uint32_t is always 32 bits. Some authors will convert that to an int others a long, etc in their stdint.h. The C language variable types remain limited to char, short, int, etc per the language (uint32_t is not a variable type it is converted to a variable type through stdint.h). This solution/workaround was a way to keep is from all going crazy and keep the language alive.

Authors will often choose for example if the GPRs are 16 bit to have int be 16 bit, and if 32 bit be 32 bit and so on, but they have some freedom.

Yes, this specifically means that there is no reason to assume that any two tools for a particular target (the computer you are reading this on for example) use the same definitions for int and long in particular, and if you want to write code for this platform that can port across these tools (that support this platform) then use the stdint.h types and not int, long, etc...Most certainly if you are crossing platforms an msp430 mcu, an arm mcu, an arm linux machine, an x86 based machine, that the types, even for the same "toolchain" (gnu gcc and binutils for example), do not have the same definitions for int and long, etc. char and short tend to be 8 and 16 bits, int and long tend to vary the most, sometimes the same size as each other sometimes different, but the point is do not assume.

It is trivial to detect the sizes, for a compiler version/target/command line options, or just go the stdint route to minimize problems later.

Caye answered 10/1, 2021 at 23:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.