8 ms·
> The reason why I still like C is that it is a simple language. Simple in a sense that it is easy to express ideas and what to expect from it. For example, if
by oconnor663 5y ago
> The reason why I still like C is that it is a simple language. Simple in a sense that it is easy to express ideas and what to expect from it. For example, if you want to retrieve an array value with two offsets, one of which can be negative, in C you write arr[off1 + off2] while in Rust it would be arr[((off1 as isize) + off2) as usize].
There's an argument to be made that C is a simple language compared to some, I'll grant that. But implicit casts and the integer promotion rules really aren't a good example of simplicity. Terseness, sure, but not simplicity. For example, from https://www.cryptologie.net/article/418/integer-promotion-in-c https://www.cryptologie.net/article/418/integer-promotion-in...
uint8_t start = -1;
printf("%x\n", start); // prints 0xff
uint64_t result = start << 24;
printf("%llx\n", result); // should print 00000000ff000000, but will print ffffffffff000000
result = (uint64_t)start << 24;
printf("%llx\n", result); // prints 00000000ff000000
There are good reasons why e.g. Go, which prioritizes simplicity very highly, does not allow implicit integer casts.
- ohazi 5y agoBut if the comparison is with C++, then they would both rank equally poorly here, because C++ doesn't want to behave differently. If you write code like that in C frequently (e.g. working with system registers on embedded CPUs), you do eventually get used to explicitly casting defensively, and I agree that it's nice that you get saner defaults in e.g. Rust.
- gameswithgo 5y agoYeah implicit casts are nice when working on a toy or personal project, but if you are putting code on a rocket or a game server or a self driving car etc, etc, absolutely not.
- astrange 5y agoThis blog is by an ffmpeg developer and in my opinion you'd never have ffmpeg with that rule because everyone would have gotten bored of all the typing and stopped working on it.
- AstralStorm 5y agoRight, because people don't use smart editors that fill in the repetitive preambles. Seriously, are you talking about C, a language where you have to write code to access any matrix optimally by hand always... (or an advanced library to do it, better than BLAS, which ffmpeg does not even use)
- astrange 5y agoThere are not a lot of operations on large matrices in there. 8x8 at the most, and the memory access on those is handcrafted so language features wouldn't really help. (for instance the order DCT coefficients are stored in memory actually depends on the IDCT used, which is different on each platform and often written in assembly.)
- dvfjsdhgfv 5y agoIt's 2021, you can use a library for almost everything, it's basically the choice of the developer.
- pjmlp 5y agoNative Oberon had MPEG video decoders, so apparently that isn't an issue.
- queuebert 5y agoExplicit casts are documentation. They may be tedious, but they are clear.
- rramadass 5y agoRight, this is why i never understood why people always harped on casts in C. Small price to pay for clarifying intent.
- spankyspangler 5y agoI don't know how you're defining simplicity but the integer promotion rules are not complex at all, I would call them very simple. The problem with them is not the complexity, it's that they work fine most of the time so many people don't learn them or are not looking out for bugs with them.
- deeviant 5y ago> Terseness, sure, but not simplicity. I feel the same whenever somebody shows up talking about their favorite functional language. Sure it can be less code, but it looks like an explosion at the unicode factory. Really though, I'd much rather have a few language "gotchas" that you need to learn once, over countless gotchas in other developers code because they are using a language the lends itself to ungrokable code. C really does have the properly where you can look at the code and just know what it does. That said, I don't think C scales very well for big projects and I personally develop in C/C++/Python/ environment as a robotics system SWE. C for embedded stuff, C++ for nearly all system code, and python for the data/Deep learning stuff.
- deleted 5y ago[deleted]
- AstralStorm 5y agoC does not have that property of not having gotchas whatsoever due to how undefined behavior is handled. (Or rather not handled) plus compiler and architecture defined behavior. If you can spot undefined and other platform defined behavior always, someone probably would want to hire you as a security specialist.
- astrange 5y agoIn current C this is actually undefined behavior: uint8_t start = -1; uint64_t result = start << 24; however there's some hope of defining it once C requires 2's-complement integers. (this wasn't reasonable in the past but is now) There are worse problems with `a << b` because the shift instruction on CPUs doesn't actually treat b as an 'int', it reads the lower X bits and uses that. C can't define what X is because not only is it different in x86 and ARM but x86 doesn't even match itself - it's different for scalar and SSE ints.
- nayuki 5y agoNo, this is not always undefined behavior. uint8_t start = -1; // Your code uint8_t start = 0xFF; // Absolutely equivalent As for the expression `start << 24`, `start` will always be promoted to `signed int` because `int` is guaranteed to be at least 16 bits wide, so it can hold all `uint8_t` values. If the width of `int` is 32 bits or narrower, then `start << 24` (which is 0xFF000000) will put a binary 1 into the sign bit, which is indeed undefined behavior. Otherwise if `int` is 33 bits or wider, then `start << 24` will fit in `int` nicely and be fully defined behavior. Reasoning about these complicated cases involving implicit conversion (-1 -> 0xFF), promotion (uint8_t -> signed int), and implementation-defined bit widths (how wide is int?) is exactly why I dislike dealing with C and C++.
- flowerlad 5y agoExplicit casts are problematic. Here's an example: int32 a; int16 b; //... b = (int16)a; Later you decide to change b to int32. int32 a; int32 b; //... b = (int16)a; Now you have a bug. If you didn't have that explicit cast the program would have worked as expected.
- gameswithgo 5y agothe first program might not have worked as expected though.
- klodolph 5y agoBut, that's a good example of why implicit casts are bad! b = (int16)a; This contains two casts... one explicit cast to int16, and one implicit cast back to int32. In languages without implicit casts, this would be an error.
- astrange 5y agoMany languages "without implicit casts" e.g. Java still have this problem because they allow widening casts. I don't think it makes sense to ban them in a language with powerful enough checks, like dependent types. If you have two variables and one is typed "int 1-10" and the other is typed "int 1-5", and it fails at runtime if it doesn't fit in the destination range, why do you need to type something that says you really meant to assign one to the other? Just assign it.
- klodolph 5y ago> Many languages "without implicit casts" e.g. Java still have this problem because they allow widening casts. I'm reading this as, "Many red boxes are blue circles." If the language allows implicit widening casts, it has implicit casts. I don't think more powerful checks are necessary. It's just that the implicit conversions in C are a bit wild, and they result in unexpected behavior and surprise programmers, and from experience, making all casts explicit is not such a burden (except for stuff like char ptr -> const char ptr). I think the fantasy here is simple... as much as we want to explore new ways to make safe programs with better type systems and runtime checks, there is still some design space near C which is safer but not really more complicated.
- beached_whale 5y agoMore so, the integer promotion rules of unsigned char and unsigned short are wrong and introduce potential signed integer overflow(in the case of unsigned short). If they were simple, they would have promoted to unsigned
- pcwalton 5y agoThis is the difference between "simple" and "easy", as Rich Hickey so eloquently put it. C--this part of it, at least--is easy. You can write code without having to think about the different integer types, thanks to the complex integer promotion rules. Rust and Go are simple: the semantics are clear and understandable, at the cost of requiring the programmer to think about this aspect of numeric types. As programmers, we often mistakenly think that simple decisions automatically lead to an easy interface for the user; sometimes they do, but not always. (On balance, I personally think that not having the implicit conversions, as Rust and Go do, is generally the better decision, but I certainly acknowledge that C is easier in this regard.)
- pasabagi 5y ago> (On balance, I personally think that not having the implicit conversions, as Rust and Go do, is generally the better decision, but I certainly acknowledge that C is easier in this regard.) Really? I think there must be a better way - I also don't like implicit conversions, but the rust approach gets extremely verbose, especially if you want to check for truncation etc.
- josephcsible 5y agoWhat's your justification for saying the second printf "should print 00000000ff000000"?
- kelnos 5y agoNot the parent, but my take on it is that you have only unsigned types in all of those signatures, but C goes and promotes the u8 to a signed quantity before doing the shift. What I believe most people would expect is that 0xff would get shifted up, and that's it, but instead you end up with that plus a sign extension to fill out the "new" 32 bits at the top of the 64-bit value.
- kazinator 5y agoSince int is allowed to be as little as 16 bits wide, and start has promoted to int, start << 24 could be undefined behavior. How it should work is that type of "start << 24" should be synthesized from the operands, without any idiotic "promotion" rule: it should therefore type uint8_t. The shift should then require a diagnostic that it exceeds the width of the type. No undefined behavior nonsense; if the shift amount is a constant then it is statically diagnosable against the width of the type, and therefore should be. This diagnostic will inform the programmer that their intent isn't being expressed.
- astrange 5y agoThe type of "uint8_t << 1" being "uint8_t" would be confusing because it would always lose the top bits (unlike the current definition), but presumably the intent is to insert it into a larger bitfield or multiply by 2. An alternative safer model called as-if-infinitely ranged integers solves this problem: https://resources.sei.cmu.edu/library/asset-view.cfm?assetid=9019 https://resources.sei.cmu.edu/library/asset-view.cfm?assetid...
- kazinator 5y agoNote that, say, SHL 1, AL ;; x86 assembly will not affect any part of the EAX register other than the low 8 bits specified by AL. This is very simple and obvious. An operation involving uint8_t staying entirely in that type is a complete no-brainer. If you want to shift bits out of the uint8_t, you cast the value to something wider, and shift that. And note that this is what you have to do when working with types that are as wide as int, or wider, which do not promote. uint32_t << 1 will lose the top bits! Losing promotion would make everything consistent: uint8_t << 1 loses the top bit the same way like uint16_t << 1 and uint32_t << 1. "As if infinitely ranged" (AIIR) is an obvious idea but it has flaws. One is the run-time checking that it requires, which is unattractive in a C-like language. The other is semantic issues. In a language like C, the values of calculations eventually have to settle into typed boxes. Under AIIR, these are not equivalent: int x = 42; int y = x * x + 3; int z = 2 * y / x; int z = 2 * (x * x + 3) / x; In the second version, we have not bound the (x * x + 3) term into a variable, but inlined it into the z calculation. Therefore, its result of that subexpression permitted to be outside of the range of int by AIIR. This is completely unacceptable, though; simply introducing a variable of the precisely correct type to hold the value of an intermediate calculation changes the semantics of the calculation. Only the values being juggled in the AIIR, so to speak, benefit; not values that have landed.
- Nginx487 5y agoI would say, it's rather primitive than just simple language. Lack of absolutely necessary features like generics, not to tell about memory and thread unsafety by design. There's zero sense to start a new project in C in 2021