5 ms·
We've had to wait till C99 to get stdint.h, clearly portability is kind of an afterthought in C. C is meant to map easily to a wide range of hardware without o
by simias 9y ago
We've had to wait till C99 to get stdint.h, clearly portability is kind of an afterthought in C.
C is meant to map easily to a wide range of hardware without overhead, conceptually it's almost the opposite of portability since it precludes creating standard abstractions that would hold true across architectures as those could not map to native functionality across the board.
It's easy to compile C programs on a different architecture however, which is probably where this argument stems from. Remember when we switched from 32 to 64bits? How many codebases managed to make the jump painlessly?
- barrkel 9y agoIndeed; the language is portable. Programs written in the language require great effort to be themselves portable. Also, portability isn't a binary attribute. For every way two platforms vary, there are often a subset of states and transitions that are identical, and two disjoint subsets that are different. As long as your inputs start out in the identical subset and don't wander too far, you're good to go. For example, if you never put a value above 32767 into an int, you don't really care whether your int is 16 or 32 bits in size. If that int is e.g. line number in a file, it may mean that your program works fine on most files, but only the 32-bit version works correctly on large files. And of course the cleverer you are the closer you're likely to skate to the edge of the common intersection.
- int_19h 9y ago> We've had to wait till C99 to get stdint.h, clearly portability is kind of an afterthought in C. C provided "at least N bits" guarantees for its integer types since its first standard (for the curious: char at least 8-bit, short at least 16-bit, long at least 32-bit; in C99, long long as least 64-bit). This is the most that you can get if you want absolute portability, because there are architectures out there where you simply can't address memory in, say, 8-bit chunks (see SHARC for an example). Consequently, while stdint.h defines types like int32_t, they are "conditionally supported", and so a C program that has a hard dependency on them is not universally portable. Types like int32_least_t and int32_fast_t are unconditionally supported, but they don't give you anything that you didn't have before.
- snakeanus 9y agoIt's actually int_least32_t and int_fast32_t.
- int_19h 9y agoI stand corrected! (Ironically, this also shows just how much these types are actually used...)
- raverbashing 9y agoGood explanation And to me this shows clearly that we shouldn't be using C then. If an arch can't address an 8 bit sized element there are two choices for what happens if you write: char *a = "test"; char x = a[1]; Either char takes 32bytes or the a[1] read will shuffle the bytes to get a 32bit value whose least significant digits are a[1] And saying that int32_t are "conditionally supported" is really proof we're making C worry about 8-bit microcontrolers when we're doing big application with it, it's trying too hard
- taneq 9y agoPretty sure 'char' in this case would be 32 bits. Arguing that C shouldn't support microcontrollers seems a bit unfair. What should embedded software engineers use, if not "high level assembler"? There are plenty of modern languages that sacrifice performance in order to be fully hardware agnostic.
- simias 9y agoI agree that the jab at microcontrollers is unwarranted (after all, I do want to be able to program microcontrollers in Rust!) But you could still have any-bit integers even on an 8 bit processor, you'd just have to emulate them. Clearly that's not really in the spirit of C which is probably why the standard doesn't force the compiler to implement those, instead relying on the coder to make a decision. But that just proves my original point: C is not about portability. It's about writing fast code on any architecture. If portability was the concern the standard would make fixed-width integers mandatory and would define things like signed overflow, forcing the compilers to emulate the behavior when the hardware doesn't behave in the same way. That's what portable languages do, that's what Rust does.
- simias 9y agoI can't edit this comment anymore but I'm replying to myself to point out this little bit of C "trivia": the printf function itself, probably the most iconic standard function of all, is not portable. The problem is that it returns an "int" which contains a negative value on errors and the number of character written on success. Of course that means that it's possible to write a string whose size doesn't fit in an int. For instance if you have a 64bit computer (and enough RAM) you can try the following code: #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <string.h> int main(void) { size_t len = 0x80000001UL; char *str = malloc(len); assert(str); memset(str, 'a', len); str[len - 1] = '\0'; fprintf(stderr, "\n%d\n", printf(str)); return 0; } Don't forget to redirect stdout to /dev/null. You can pipe through `wc -c` if you want to make sure the billion 'a's are printed. On my computer the "printf" manages to print the whole string successfully but it returns... -1, signaling an error. The size shouldn't be returned that way, it should be a "size_t" as an output parameter (since you also need a special value for errors). In Rust you'd use "Result<usize>" and you'd have a more elegant solution. Of course you can't really do that in C (except by returning a struct, which is a bit cumbersome) so I guess they said "meh, who prints that much text anyway?" and assumed an int would be good enough.