Live data from Hacker News

Everything I wish I knew when learning C

tmewett.com

221–230 of 401 posts

Re: Everything I wish I knew when learning C

#221
post #111
post #98

When I first learned C - which also was my first contact with programming at all - I did not understand how pointers work, and the book I was using was not helpful at all in this department. I only "got" pointers like three or four years later, fortunately programming was still a hobby at that point. Funnily when I felt confident enough to tell other people about this, several immediate started laughing and told me w…

Having basic experience in any assembly language makes pointers far more clear. "Addressing modes," where a register and some constant are used to calculate the source or target of a memory operation, make the equivalence of a[b]==*(a+b) much more obvious. I also wonder about the author's claims that a char is almost always 8 bits. The first SMP machine that ran Research UNIX was a 36-bit UNIVAC. I think it was ASCII…

There was an "official" C compiler for NOS running on the CDC Cyber. As I recall, 18-bit address, 60-bit words, more than one definition of a 'char' (12-bit or 5-bit, I think). It was interesting. There were a lot of strange architectures with a C compiler.

I would also point out architectures like the 8051 and 8086 made (make...they are still around) pointer arithmetic interesting.

Re: Everything I wish I knew when learning C

#222
post #122

I find this article very strange, perhaps because I started using 'c' so long ago. To the bullet points: (1) In general, 'c' is always 'c' at the command line, regardless of the platform. (2) yes, there are options and build tools, but cc my_program.c -o my_program works fine. I have a very hard time figuring out how to compile/run java. (3) hard to see how this has anything to do with 'C', vs any other compiled lang…

Now I need to make a C dialect with better string handling built-in and call it 'C'

Agreed, I tore my hair out trying to figure out strings in C

Re: Everything I wish I knew when learning C

#223
post #156

I like it, but the array details are a little bit off. An actual array does have a known size, that's why when given a real array `sizeof` can give the size of the array itself rather than the size of a pointer. There's no particular reason why C doesn't allow you to assign one array to another of the same length, it's largely just an arbitrary restriction. As you noted, it already has to be able to do this when assi…

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

> But assignment is always a very cheap operation in C.

That's just not true though, you can assign struct's of an arbitrarily large size to each-other and compilers will emit the equivalent of `memcpy()` to do the assignment. They might actually call `memcpy()` automatically depending on the particular compiler.

The fact that if you wrap the array in a struct then you're free to copy it via assignment makes it arbitrary IMO.

Re: Everything I wish I knew when learning C

#224
post #203

Earlier quoted context omitted.

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…

This is the problem; every compiler outcome is a series of small logic inferences that are each justifiable by language definition, the program's structure, and the target hardware. The nasal demons are emergent behavior. It'd be one thing if programs hitting UB just vanished in a puff of smoke without a trace, but they don't. They can keep on spazzing out literally forever and do I/O , spewing garbage to the outside…

It can get truly bizarre with multiple threads. Some other thread hits some UB and suddenly your code has garbage register states. I've had someone UB the fp register stack in another thread so that when I tried to use it, I got their values for a bit, and then NaN when it ran out. Static analysis had caught their mistake, and then a group of my peers looked at it and said it was a false warning leaving me to find it long afterwards... I don't work with them anymore, and my new project is using rust, but it doesn't really matter if people sign off on code reviews that have unsafe{doHorribleStuff()}

Re: Everything I wish I knew when learning C

#225
post #177

I like it, but the array details are a little bit off. An actual array does have a known size, that's why when given a real array `sizeof` can give the size of the array itself rather than the size of a pointer. There's no particular reason why C doesn't allow you to assign one array to another of the same length, it's largely just an arbitrary restriction. As you noted, it already has to be able to do this when assi…

> There's no particular reason why C doesn't allow you to assign one array to another of the same length, it's largely just an arbitrary restriction. IIRC C has an informal guarantee that no primitive syntax will ever cause the CPU to do more than O(1) work at runtime. Assignment is always O(1), and therefore assignment is limited to scalars. If you need assignment that might do O(N) work, you need to call a stdlib f…

> Assignment is always O(1)

This depends on what you consider to be O(1) - being that the size of the array is fixed it's by definition O(1) to copy it, but I might get your point. I think in general your point isn't true though, C often supports integer types that are too large to be copied in a single instruction on the target CPU, instead it becomes a multi-instruction affair. If you consider that to still be O(1) then I think it's splitting hairs to say a fixed-size array copy would be O(N) when it's still just a fixed number of instructions or loop iterations to achieve the copy.

Beyond that, struct assignments can already generate loops of as large a size as you want, Ex: https://godbolt.org/z/8Td7PT4af

Re: Everything I wish I knew when learning C

#226
this is a good a good blog, although I wonder is there any good up-to-date free online resource to learn C for experienced programmer (I learned C many years ago, but never use it seriously and forgot much of it)? I searched around, the results are either for beginner, or seems out-dated

Re: Everything I wish I knew when learning C

#227
I've been programming in C forever, one advantage is that the language has not evolved much (especially compared with C++), but it has evolved.

There was the big K&R C to ANSI C function declaration transition. For portable code, you used K&R C well into the 90s (because older machines only had the K&R compiler), or used ugly macros to automatically convert from ANSI to K&R.

Another was the addition of 'const' to the language. It used to be said that const was a virus: once you start using it, you need to use it universally in your entire code-base.

A more recent big one is stdint.h (types like uint32_t). To correctly use these types you must also use the macros in inttypes.h for scanf and printf conversions. IMHO, they both should have been in the same header files, they go along with each other.

So in the old days, you would say: unsigned int x; printf("%u\n", x);

But now, you should tediously say: uint32_t x; printf("%"PRIu32"\n", x);

(Because uint32_t might be defined as a long even if it's the same size as in int, so you will get compiler warnings. You could use %lu, but then you get compiler warnings the other way.)

Another: On UNIX, we don't have to worry about "wide" characters (use UTF-8 instead) and wide strings, but you certainly do on Windows. "Wide" characters are obsolete but Windows is stuck with them since they are built into the OS.

Re: Everything I wish I knew when learning C

#228

Earlier quoted context omitted.

Everything is easier once you torture yourself with trying to learn Assembler :)

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.

Re: Everything I wish I knew when learning C

#229
post #40

C syntax is already too rich and complex. typedef/enum/(_Generic)/etc should go (fix/cleanup function pointer type declaration). Only sized primitive types (u32/s32,u64/s64,f32/f64 or udw/sdw,uqw/sqw,fdw/fqw...). We would have only 1 loop statement "loop{}", no switch. I am still thinking about "anonymous" code blocks for linear-code variable sub-scoping (should be small compile-unit local function I guess). No integ…

I sympathize a bit with the dislike of enum. We could also probably get away with replacing typedef's with either #define (for simple types) or one-field structs (for arrays and function pointers; the latter usually need a void* to go along with them anyway). There are reasonable low-level optimizations you can do that switch is needed for. You can have cases that start or end around blocks in non-hierarchical ways.…

This is a very slipery slope (where gcc/clang[llvm] just run onto): adding tons of attributes/keywords/"syntax features" which gives the compiler some semantic hints in order to perform "better" optimizations. There is no end to it, this is a toxic spiral of infinite planned obsolescence (and now ISO is doing the same).

There is also this other thing: extreme generalization and code factorization, to a point, we would have no clue of what the code actually does without embracing the entirety of the code with its "model". It did reach a pathological level with c++.

And the last, but not the least: OS functions are being hardcoded in the syntax of the language.

If you push further all those points they kind of converge: compilers will have keywords specific for each performance critical syscall, significant library function, and significant data structure for abstraction (some abstraction can be too much very fast as I said before). There is a end game though: directly coding assembly.

Re: Everything I wish I knew when learning C

#230

Earlier quoted context omitted.

Pointers are by far the most insidious thing about C. The problem is that nobody who groks pointers can understand why they had trouble understanding them in the first place. Once you understand, it seems so obvious that you cannot imagine not understanding what a pointer is, but at the beginning, trying to figure out why the compiler won't let you assign a pointer to an array, like `char str[256]; str = "asdf"`, is…

What is so difficult about the concept of a memory address? Is it the C syntax? Asking because I personally have never struggled with this.

That's the problem! I can't tell you what is difficult because it seems so incredibly obvious to me now.

When I was ~12, I had a lot of trouble with it, and the only thing I remember from those times is various attempts to figure out why the compiler wouldn't let me assign a string value to an array. What the hell is an "lvalue", Mr. Compiler?

Now I look at the assignment command above and I recoil in horror, but for some reason at the time it seemed very confusing to me, especially since `char *str; str = "abcd";` works so well. The different between the two (as far as intention goes) is vast in retrospect, but for some reason I had trouble with it at the time.

Post reply on HN