Back to News
Advertisement
Advertisement

⚡ Community Insights

Discussion Sentiment

67% Positive

Analyzed from 1855 words in the discussion.

Trending Topics

#bits#bit#integer#int#size#fixed#sizes#types#long#code

Discussion (40 Comments)Read Original on HackerNews

adrian_b•about 2 hours ago
I agree that the C flexible integer sizes were still necessary at the time of its creation, when some important computers still had word sizes that were not powers of two.

Nonetheless, I started to use C for programming only in 1990, when I got access to the Microsoft C and Borland Turbo C compilers.

At that time, 36 years ago, the C flexible integer sizes were already obsolete.

Since that time until now, while using C on a great variety of computers, from servers and workstations to the smallest microcontrollers, I have seen plenty of portability problems created by the existence of the flexible integer sizes.

The only programs that had no portability problems were those that never used the flexible integer sizes, but only integers with a definite size, e.g. 8-bit, 16-bit, 32-bit or 64-bit.

While sizeof solves the problems of memory allocation or copying, it does not help in preventing unexpected integer overflows, because even the size of "char" may be unknown, and even if the size of "char" is known, writing code with multiple paths that would check or prevent overflow for different integer sizes is very cumbersome.

Flexible integer sizes would work well only on the old computers, where integer overflow generated a hardware exception, so installing an overflow handler would have been sufficient to make the C code work correctly regardless of the size of the native integers.

nickcw•16 minutes ago
The fixed size int types int32_t and friends weren't introduced until C99. Microsoft held out until 2013 before it put <inttypes.h> into Visual Studio!

So there has been a really long time in C's evolution where we haven't had fixed size types which has been a super annoying mess of #ifdefs in portable code.

The variable size ints have allowed some super weird architectures though. I remember looking at the datasheet for the Motorola 56000 DSP and noting that the C compiler set char = short = int = 24 bits! That was because the hardware could not address anything smaller than 24 bits. I think long could be 48 bits.

ddlsmurf•14 minutes ago
I would agree if overflow on those types wasn't undefined behaviour, or unpredictable
jibal•29 minutes ago
Per the C standard `sizeof(char)` is always 1, regardless of how many bits it has.
ddlsmurf•12 minutes ago
because it's the unit of addressable memory that C is concerned by with sizeof, otherwise its values still depend on CHAR_BIT
locknitpicker•about 2 hours ago
> At that time, 36 years ago, the C flexible integer sizes were already obsolete.

This is a highly ignorant comment. You're confusing the fact that you only had to work with a single target architecture with the whole concept of multiple processor architectures being somehow obsolete, as if there was a sudden law of nature that forced every single computer, being full blown HPC stuff or small microcontrollers used in embedded applications.

Take a look at arduino. They still have 16-bit models out there. Also noteworthy, it seems some DSPs also have ints larger than 32 bits.

flohofwoe•about 1 hour ago
The parent is completely right in the sense that for actually portable C code it was always better to use fixed-width integer types which were chosen for the problem to solve instead of target hardware capabilities.

For instance if your integer arithmetic needs to happen with 32 bit precision (no matter if the code runs on a 16- or 32-bit CPU), there is no scenario where using 'int' makes sense. Instead you'd use a fixed-width 32-bit integer type and accept that math operations are compiled into two instructions on a 16-bit CPU.

And OTH if you only require 16 bits integer width, there's not much point in picking a 32 bit integer type. Since two's-complement integer encoding has been standard since at least the 70s, the CPU can do narrow operations in the native register width. Any overflow/wraparound is still correct when only looking at the lowest 16-bits of the result.

adrian_b•about 1 hour ago
As I have said, I have not worked with a single architecture.

Before 1990, I had worked with a variety of ISAs, from IBM mainframes and DEC minicomputers to many kinds of microprocessors.

After 1990, I have used C on a great variety of x86, Motorola 68xxx, IBM/Motorola PowerPC and many generations of ARM ISAs.

Even if you use explicit 32-bit integers in a program, that will not create any correctness problem when the program is run on 16-bit microcontroller. At most such a program may have a suboptimal performance. Performance problems are much easier solved during porting than obscure bugs.

There have been some popular DSPs with 24-bit integers, e.g. Motorola 56xxx. Nonetheless, nobody would want to run on such a DSP a program that was written for another kind of CPU, even for another kind of DSP, because the performance would be pathetic. Any program for such a fixed-point DSP, even when derived from an existing program, would need to be rewritten while using at every point in the program the knowledge that the size of "int" is 24 bits (because the programs for fixed-point DSPs need copious amounts of scaling operations, to avoid overflows and underflows), so such a program should not actually use "int", but it should typedef an "int24_t", to make this assumption explicit.

flowerbreeze•about 2 hours ago
Thank you for the article! Do I see a Turbo-C screenshot there or am I imagining it? It was my first IDE (I didn't know that's what it was called) when I started programming. I sometimes miss it, it was really good, especially the help system.

I agree with the article of course. I think most confusion comes not having learned about the purpose of having them be defined based on the architecture in the first place. It took me a long time before I stumbled upon how they really worked and why, because while I started it was either x86 or nothing. When x64 showed up, suddenly it became relevant and everybody started learning about C types more in depth as they ran into issues with sizeof.

Also, misuse in data protocols is where I think the bad reputation of the flexible type sizes came from. stdint was desperately needed for that reason and it came a bit late.

RobotToaster•about 2 hours ago
> A 'plain' int object has the natural size suggested by the architecture of the execution environment.

Shouldn't they be 64 bits on most modern systems then?

userbinator•about 2 hours ago
On x86-64, you need an extra prefix to do 64-bit operations (while 64-bit addressing is the default), so it's a question of "are you sure you need the 64 bits and 32 isn't enough?"
entrope•about 1 hour ago
> Shouldn't they be 64 bits on most modern systems then?

Arguably so, but then one would lose the ability to natively name 16-bit integer types because "short" would be 32 bits.

An earlier comment addresses x86-64. AArch64 (pedantically, the A64 instruction set used for AArch64's 64-bit execution mode) is similar, in that addresses are 64 bits wide but ALU instructions typically encode a width bit, called "sf", that selects either 32- or 64-bit data registers and arithmetic. See, for example, https://arm.jonpalmisc.com/latest_aarch64/add_addsub_ext .

imtringued•25 minutes ago
char is at least 8 bits, short is at least 16 bits, long is at least 32 bits, long long is at least 64 bits.

Not sure how what you said makes sense.

flohofwoe•about 2 hours ago
It should indeed, and in hindsight it would have been better to move int to 64 bits (especially for C's integer promotion, which only really makes sense when the promotion happens to the register width.

But porting 32-bit code to 64-bit was a big deal back then, and C99 with its new fixed-width integer types overlapped with the first AMD64 CPUs (and Microsoft's MSVC didn't start to support C99 until around 2015 anyway), I guess keeping int on 32-bits in the popular compilers was deemed 'safer' for porting existing code. I guess we can already be lucky that all the big compilers agreed on the same int width.

astrobe_•about 2 hours ago
I've always believed that they kept 32 bits ints on 64 bits CPU as a default because going full 64 bits would make the code and data structures bigger for "no reason" (it's not often that one hits the 4.10^9 limit in system code (if you don't count timestamps, that is)). For instance, a load-register-with-immediate instruction would normally take 5 bytes (opcode+value) on 32 bits, but 9 bytes on 64.
Alpha3031•about 2 hours ago
Memory addresses being 64 bits due to needing to address more than 4 GiB memory doesn't mean most integer instructions operate most efficiently with 64 bits. Instructions for 32 bit integers are still more efficient than 64 bit, whereas 16 bit operands require a prefix byte meaning they're less compact and cache efficient (on AMD64 anyway).
quelsolaar•about 2 hours ago
Good article.

C would probably not have survived unless it had this flexibility.

But its not justa historical thing. Today there are modern platforms like DSPs that have 32bit sized char, because that is the smallest addressable type. These platforms depend on C for tool chains, even if most "portable" C wont run correctly on them. The fact that you can build hardware like that, and not have to invent a new language / dialect to program them is a huge win for the world.

<edit> I didnt see the footnote about DSPs at first read </edit>

usrnm•about 2 hours ago
But not having fixed size integers (or integers tied to the size of a pointer) was. Both can be useful
quelsolaar•about 2 hours ago
At the time its was probably very hard to know what the fixed sizes should be.
adastra22•about 1 hour ago
No one thinks that ptrdiff_t should be a fixed size. It is quite obviously the integer type you would get from subtracting two pointers, which is naturally tied to the word size of the machine you are using. C's original "int" type is what we would now call ptrdiff_t.
imtringued•29 minutes ago
>This is a deliberate design statement. int was never meant to be "32 bits". It meant "whatever this machine is fastest and most comfortable with".

This is the issue with how people talk about C. int is basically the signed version of size_t aka a word sized data type. It's not meant to have a fixed size.

When people want the classic 4 byte data type they should choose long instead.

sparkie•17 minutes ago
> int is basically the signed version of size_t aka a word sized data type. It's not meant to have a fixed size.

It isn't. `int` is at least as large as `short` and at least 16-bits. On modern systems `int` is still typically 32-bits whereas `size_t` is typically 64-bits. There's a `ssize_t` in POSIX for signed sizes.

> When people want the classic 4 byte data type they should choose long instead.

`long` is only at least 4 bytes, and at least as large as `int`. On MSVC (LLP64 data model) it's 4 bytes, but on SYSV (LP64 data model) it's 8 bytes. `long` should almost never be used if you actually want portable code today.

`int` is 32-bits and `long long` is 64-bits on both LP64 and LLP64. If you want portable code using the native integer types, these are the ones you should use, definitely not `long`.

pjmlp•about 2 hours ago
Kind of, the mistake was not doing like PL/I where besides default machine specific sizes, the developer could explicitly assert the required sizes.
astrobe_•about 1 hour ago
Is that really that much of a big deal, though? Before stdint, if one needed that level of accuracy, one would do your own equivalent of stdinit by hand, and adjust those definitions when porting to another compiler/platform. The same goes for your local boolean type.

I think the only real annoyance is that each programmer/team did it with their own convention (I32, INT32, i32, int32, WORD, Word bool, BOOL, Bool, etc., etc.); standardizing helps with putting everyone on the same page more than it helps porting. It doesn't prevent people from reverse-typedef-ing standard names to local "dialectal" names, though.

But I also think that one should only rarely use raw integer types, in an ideal world; the elephant in the room is that typedef is kind of the second "billion dollars mistake" [1]. C is a weakly typed language and there's no practical way to undo it (besides transpilation), so there's double no point to leave behind raw types.

[1] For those not too familiar with C, typedef defines a "type alias", not a type: https://en.cppreference.com/cpp/language/typedef

pjmlp•about 1 hour ago
The deal was dealing with #ifdef spaghetti to define all of those, especially when mixing libraries across platforms.
flohofwoe•about 2 hours ago
C99 kinda fixed that with the `(u)int_leastN_t` types (which are hardly used in practice though). And shame that it took Microsoft 16 years to even start supporting C99 though so we were basically forced to keep using our own custom integer typedefs long after the C standard had fixed the issue.
pjmlp•about 1 hour ago
Agreed, but it could have been there since day one, given the languages in 1960's.

Well, Microsoft has considered C done for quite some time, and after C++20, they don't seem to be in a hurry to keep up with ISO either for C or C++ (there are discussions on support channels about customer relevant C++23 and C++26 features, none on C past C17), similar to how Apple and Google are handling their in box compilers as well.

Alpha3031•about 2 hours ago
I thought most implementations of C have stdint (intN_t, leastN_t and fastN_t etc).
flohofwoe•about 1 hour ago
These are C99 features which MSVC only got around 2015 (of course a decade later those are safe to use in portable code).
gustavopezzi•about 19 hours ago
Author here. Thanks for sharing.
enriquto•about 2 hours ago
it's a great read!

would like to learn some tricks, like configuring gcc so that int is 64 bits, and so on

lexicality•about 2 hours ago
I feel like the article glosses over the fact that (to my mind) `int_fast32_t` and `int_least32_t` are a much better solution than "int is a random size good luck"

If you code exclusively using those types (and the `*ptr_t` ones) then you precisely express to both the compiler and the next person reading it what is supposed to be in those variables.

mmoll•about 1 hour ago
I came here to say exactly that. There’s int_leastN_t for storage and int_fastN_t for computation. Stdint.h really gets a bad rap here.
lexicality•about 1 hour ago
fwiw depending on use cases you might actually want to be using the fast variants for storage too, for example on arm64 you'll get aligned memory loads
lmz•about 2 hours ago
Meh. In today's world if exact sizes were not a requirement then you should use the int_fastN_t types to at least guarantee the width you are expecting instead of using the fixed size types (which may not be optimal) or using plain "int" which may be smaller than expected.
Advertisement