The couple of times I tried to really learn C I ran into the same problems. I started by trying to answer two questions: what are the modern features of C that I need to learn to use it, and what style/rules should I follow. What I found is a body of several C spec updates that are each defined in reference to previous C spec updates. So I'm supposed to... ? Learn C from the 70s and then study each version update and…
Everything I wish I knew when learning C
271–280 of 401 posts
Re: Everything I wish I knew when learning C
#272Earlier quoted context omitted.
> Integer division is a slow operation, and under the rules of C, it has no side effects. Then C isn't following this rule - crashing is a pretty major side effect.
To my downvoters, since I can no longer edit: I've been corrected that the rule is integer division has no side effects except for dividing by zero . This was not the rule my parent poster stated.
No you haven't. The incorrect statement was a verbatim quote from nayuki's post, which you were responding to. Please refrain from apologising for other people gaslighting you (edit: particularly, but not exclusively, since it sets a bad precedent for everyone else).
Re: Everything I wish I knew when learning C
#273Earlier quoted context omitted.
> it's largely just an arbitrary restriction Kind of. But the restriction is in keeping with the C philosophy of no hidden implementation magic. C has the same restriction on structs. That's the same question; an array of bytes of known size to the compiler it could easily abstract away. But assignment is always a very cheap operation in C. If we allow assigning to represent memcpy() that property is no longer true.…
Ironically, Rust does allow you to implicitly copy an array as long as it reduces to a memcpy
The array of mutable Strings can be memcpy'd and there are situations where that's actually what Rust will do, but because Strings aren't Copy, Rust won't let you keep both - if it did this would introduce mutable aliasing and so ruin the language's safety promise.
Re: Everything I wish I knew when learning C
#274Earlier quoted context omitted.
> how insidious undefined behavior is. Indeed. UB in C doesn't mean "and then the program goes off the rails", it means that the entire program execution was meaningless, and no part of the toolchain is obligated to give any guarantees whatsoever if the program is ever executed , from the very first instruction. A UB-having program could time-travel back to the start of the universe, delete it, and replace the entire…
I'm not sure that's a productive way to think about UB. The "weirdness" happens because the compiler is deducing things from false premises. For example, 1. Null pointers must never be dereferenced. 2. This pointer is dereferenced. 3. Therefore, it is not null. 4. If a pointer is provably non-null, the result of `if(p)` is true. 5. Therefore, the conditional can be removed. There are definitely situations where many…
Re: Everything I wish I knew when learning C
#275Earlier quoted context omitted.
> how insidious undefined behavior is. Indeed. UB in C doesn't mean "and then the program goes off the rails", it means that the entire program execution was meaningless, and no part of the toolchain is obligated to give any guarantees whatsoever if the program is ever executed , from the very first instruction. A UB-having program could time-travel back to the start of the universe, delete it, and replace the entire…
I'm not sure that's a productive way to think about UB. The "weirdness" happens because the compiler is deducing things from false premises. For example, 1. Null pointers must never be dereferenced. 2. This pointer is dereferenced. 3. Therefore, it is not null. 4. If a pointer is provably non-null, the result of `if(p)` is true. 5. Therefore, the conditional can be removed. There are definitely situations where many…
I suppose I think in terms of "what would a reasonable person expect to happen with this use of UB" and do that. This probably derives, again, from my experience designing flight critical aircraft parts. You don't want to interpret the specification like a lawyer looking for loopholes.
It's the same thing I learned when I took a course in race in high performance driving. The best way to avoid collisions with other cars is to be predictable. It's doing unpredictable things that cause other cars to crash into you. For example, I drive at the same speed as other traffic, and avoid overtaking on the right.
Re: Everything I wish I knew when learning C
#276Earlier quoted context omitted.
The standard does permit time-travel, however. As unlikely as it might seem, I could imagine some rare scenarios in which something seemingly similar happens -- let's say the optimiser reaching into gets() and crashing the program prior to the gets() call that overflows the stack.
Time travel only applies to an execution that is already known to contain UB. How could it know that the gets() call will necessarily overflow the stack, before it actually starts reading the line (at which point all prior observable behavior must have already occurred)?
Re: Everything I wish I knew when learning C
#277Earlier quoted context omitted.
I really wish that int arr[5] adopted the semantics of struct { int arr[5]; } -- that is, you can copy it, and you can pass it through a function without it decaying to a pointer. Right now in C: typedef uint32_t t1[5]; typedef struct { uint32_t arr[5]; } t2; void test(t1 a, t2 b) { t1 c; t2 d; printf("%d %d %d %d\n", sizeof(a), sizeof(b), sizeof(c), sizeof(d)); } will print 4, 20, 20, 20. I understand that array typ…
> I really wish that int arr[5] adopted the semantics of struct { int arr[5]; } You and me both. In fact, D does this. `int arr[5]` can be passed as a value argument to a function, and returned as a value argument, just as if it was wrapped in a struct. It's sad that C (and C++) take every opportunity to instantly decay the array to a pointer, which I've dubbed "C's Biggest Mistake": https://www.digitalmars.com/artic…
To bad they spend most their time doing whatever it is they do.
Re: Everything I wish I knew when learning C
#278Earlier quoted context omitted.
Any optimization that causes undefined behavior is bugged – please report them to your compiler's developers.
By definition an optimisation can’t cause UB as UB is a langage level construct. An optimisation can cause a miscompilation. They happens and is very annoying.
Re: Everything I wish I knew when learning C
#279Earlier quoted context omitted.
> C specification says a program is ill-formed if any UB happens. So yes, the spec does say that compilers are allowed to assume UB doesn't happen. I disagree on the logic from "ill-formed" to "assume it doesn't happen". > I think you're conflating "unspecified behavior" and "undefined behavior" - the two have different meanings in the spec. I admit I don't differentiate those two words. I think they are just word-pl…
That's the whole point of UB though: the programmer helping the compiler do deduce things. It's too much to expect the compiler to understand your whole program to know a+b doesn't overflow. The programmer might understand it doesn't though. The compiler relies on that understanding. If you don't want it to rely on it insert a check into the program and tell it what to do if the addition overflows. It's not hard. Whi…
Given that even experts routinely fail to write C code that doesn't have UB, available evidence is that it's practically impossible.
Re: Everything I wish I knew when learning C
#280Earlier quoted context omitted.
To my downvoters, since I can no longer edit: I've been corrected that the rule is integer division has no side effects except for dividing by zero . This was not the rule my parent poster stated.
> I've been corrected No you haven't. The incorrect statement was a verbatim quote from nayuki's post, which you were responding to. Please refrain from apologising for other people gaslighting you (edit: particularly, but not exclusively, since it sets a bad precedent for everyone else).
I wrote:
> Integer division is a slow operation, and under the rules of C, it has no side effects.
This statement is correct because if the divisor is not zero, then division truly has no side effects and can be reordered anywhere, otherwise if the divisor is zero, the C standard says it's undefined behavior so this case is irrelevant and can be disregarded, so we can assume that division always has no side effects. It doesn't matter if the underlying CPU has a side effect for div-zero or not; the C standard permits the compiler to completely ignore this case.