Live data from Hacker News

Everything I wish I knew when learning C

tmewett.com

231–240 of 401 posts

Re: Everything I wish I knew when learning C

#231
post #61
post #50

Earlier quoted context omitted.

Thanks. Now I understand why I found pointers difficult. It's the declaration that confused me.

You can read a declaration like `int x` as “` x` is an int”, and hence x is an int pointer.

With the asterisks backslash escaped:

> ..read a declaration like `int *x` as "`*x` is an int"..

Re: Everything I wish I knew when learning C

#232

Earlier 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.

[deleted]

Re: Everything I wish I knew when learning C

#233

Earlier quoted context omitted.

Executing the program with that input is the key term. The program can't "take back" observable effects that happen before the input is completely read, and it can't know before reading it whether the input will be one that results in an execution with UB. This is a consequence of basic causality. (If physical time travel were possible, then perhaps your point would be valid.)

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

#234
post #74

> Everything I wish I knew when learning C By far my biggest regret is that the learning materials I was exposed to (web pages, textbooks, lectures, professors, etc.) did not mention or emphasize how insidious undefined behavior is. Two of the worst C and C++ debugging experiences I had followed this template: Some coworker asked me why their function was crashing, I edit their function and it sometimes crashes or do…

It's not insidious at all. C compiler offers you a deal: "Hey, my dear programmer, we are trying to make an efficient program here. Sadly, I am not sophisticated enough to deduct a lot of things but you can help me! Here are some of the rules: don't overflow integers, don't dereference null pointers, don't go outside of array bounds. You follow those and I will fulfill my part of making your code execute quickly".

The deal is known and fair. Just be a responsible adult about it: accept it, live with the consequences and enjoy efficiency gains. You can reject it but then don't use arrays without a bound check (a lot of libraries out there offer that), check your integers bounds or use a sanitizer, check your pointers for nulls before dereferencing them, there are many tools out, there to help you, or... Just use another language that does all that for you.

Re: Everything I wish I knew when learning C

#235
post #228

Earlier quoted context omitted.

Definitely pick a simpler assembly than x86 assembly, and it's not so bad. I learned 68HC11 assembly which has been a boon for understanding what's happening underneath the hood.

Even today, I still find 68000 the most comfortable. It is almost easier than writing C.

Hmm speaking of nostalgia, are there hobby z80 boards? Perhaps with enough peripherals to run cp/m?

I do mean real z80s not emulators.

Re: Everything I wish I knew when learning C

#236

" ... is often called the stack." " integers are cursed" These statements and a few others made me uncomfortable. They imply, to me, that the author has too little knowledge of computer internals to be programming in C. C does a wonderful job of looking like a high level language but it was designed to do low level stuff. There is an implicit assumption, to my mind, that the user has a deeper understanding of what is…

Unsolicited personal opinion/anecdote incoming:

As someone who started writing C with very little understanding of how the underlying hardware worked (or, indeed, programming in general), I support and disagree with parts of this comment at the same time.

On one hand, I support the notion that programming well (in any language) requires knowledge of hardware architectures.

On the other hand, I disagree that people should not be "allowed to write a line of C" before they have that understanding.

I started writing C early on in my programming career (having already dropped out of "Introduction to Computer Hardware"), and I'll admit, it was tough. I probably would have had an easier time if I had taken a year to study x86, win32 internals and graphics pipelines. That said, I was interested in learning to program graphics, so that's what I did, and I learned a tremendous amount while doing it. It was probably the long way round, but if the goal was learning, "just doing it" was an effective strategy.

What I'm trying to say here is that for people that would drop out of "Introduction to Computer Hardware", learning C is actually an extremely good supplement for learning about underlying hardware architectures because, in the long run, you have no choice if you want to be good at it.

Re: Everything I wish I knew when learning C

#237
post #7

This looks decent, but I'm (highly) opposed to recommending `strncpy()` as a fix for `strcpy()` lacking bounds-checking. That's not what it's for, it's weird and should be considered as obosolete as `gets()` in my opinion. If available, it's much better to do the `snprintf()` way as I mentioned in a comment last week, i.e. replace `strcpy(dest, src)` with `snprintf(dst, sizeof dst, "%s", src)` and always remember tha…

>sizeof dst Note that this only works if dst is a stack allocated(in the same function) array and not a char *

> and always remember that "%s" part. Never put src there, of course

> Note that this only works if dst is a stack allocated array

Even this "ideal" solution is full of pitfalls. The state of memory safety is so sad in the world of C.

Re: Everything I wish I knew when learning C

#238
post #202

The reason you use specific sizes for types (int8, int16, uint16, etc) is so you know how to read/write them when you move your data between platforms. x86 is little endian. ARM apparently can be configured to be either. In real code there should be readXXX and writeXXX functions that read/write data from disk/network and do the byte swapping in there. You could also just convert everything to JSON, but you're tradin…

> ARM apparently can be configured to be either.

That's insane!!! TIL

Re: Everything I wish I knew when learning C

#239
post #228

Earlier quoted context omitted.

Even today, I still find 68000 the most comfortable. It is almost easier than writing C.

Hmm speaking of nostalgia, are there hobby z80 boards? Perhaps with enough peripherals to run cp/m? I do mean real z80s not emulators.

The Retrobrew community has designed a few of them, even as SBCs[0]. There's definitely more elsewhere.

0. https://www.retrobrewcomputers.org/doku.php?id=boards:sbc:st...

Re: Everything I wish I knew when learning C

#240

Earlier quoted context omitted.

> It is definitely not, e.g., an assertion on the operand because UB can't happen. 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. After all, a program with UB is ill-formed and therefore shouldn't exist! I think you're conflating "unspecified behavior" and "undefined behavior" - the two have different meanings in the sp…

> 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.

Whining about UB is like reading Shakespeare to your dog and complaining it doesn't follow. It's not that smart. You are though. If you want it to check for an overflow or whatever there is a one liner to do it. Just insert it into your code.

Post reply on HN