Live data from Hacker News

Everything I wish I knew when learning C

tmewett.com

111–120 of 401 posts

Re: Everything I wish I knew when learning C

#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, but the OS2200/EXEC8 SMP matured in 1964, so this was an old architecture at the time of the port.

"Any configuration supplied by Sperry, including multiprocessor ones, can run the UNIX system."

https://www.bell-labs.com/usr/dmr/www/otherports/newp.pdf

Re: Everything I wish I knew when learning C

#112

After more than a decade without writing any C code, I'm currently reading "C Programming: A modern Approach" by K. N. King ( http://www.knking.com/books/c2/ ) and I found it very good. I think it's a better modern alternative to the K&R.

832 pages is not a "better alternative."

Re: Everything I wish I knew when learning C

#113
post #106

> C has no environment which smooths out platform or OS differences Not true - C has little environment, not no environment. For example, fopen("/path/file.txt", "r") is the same on Linux and Windows. For example, uint32_t is guaranteed to be 32 bits wide, unlike plain int. > Each source file is compiled to a .o object file Is this a convention that compilers follow, or are intermediate object files required by the C…

> Is this a convention that compilers follow, or are intermediate object files required by the C standard? Does the standard say much at all about intermediate and final binary code?

The standard only says that the implementation must preprocess, translate, and link the several "preprocessing translation units" to create the final program. It doesn't say anything about how the translation units are stored on the system.

> This keyword is funny because in a global scope, it reduces the scope of the variable. But in a function scope, it increases the scope of the variable.

Not quite: in a global scope, it gives the variable internal linkage, so that other translation units can use the same name to refer to their own variables. In a block scope, it gives the variable static storage duration, but it doesn't give it any linkage. In particular, it doesn't let the program refer to the variable outside its block.

Re: Everything I wish I knew when learning C

#114
post #79

Earlier quoted context omitted.

Was rewriting the stack due to undefined behavior or was it due to a logic error, e.g. improper bounds calculation?

Isn’t all UB a result of logic errors? Writing beyond the end of allocated memory (due to incorrect bounds calculation ) is an example of undefined behaviour

There are some complicated UBs that arise when casting to different types that are not obviously logic errors (can't remember the specifics but remember dealing with this in the past).

Re: Everything I wish I knew when learning C

#115
post #100

Earlier quoted context omitted.

> -fsanitize=address,undefined In addition, I recommend -fsanitize=integer. This adds checks for unsigned integer overflow which is well-defined but almost never what you want. It also checks for truncation and sign changes in implicit conversions which can be helpful to identify bugs. This doesn't work if you pepper your code base with explicit integer casts, though, which many have considered good practice in the p…

Good one, thanks. Note that it requires Clang; GCC 12.2 doesn't have it.

Wow nice, I didn't know about this one. I can add some more which are less known. This is my current sanitize invocation (minus the addition of "integer" which I'll be adding, unless one of these other ones covers it):

  -fsanitize=address,leak,undefined,cfi,function
CFI has checks for unrelated casts and mismatched vtables which is very useful. It requires that you pass -flto or -flto=thin and -fvisibility=hidden.

You can read a comparison with -fsanitize=function here:

https://clang.llvm.org/docs/ControlFlowIntegrity.html#fsanit...

There's also TypeSanitizer, which isn't officially released, but is really interesting and should be able to be applied via a patch from the branch:

https://www.youtube.com/watch?v=vAXJeN7k32Y

https://reviews.llvm.org/D32199

  $ curl -L 'https://reviews.llvm.org/D32199?download=1' | patch -p1

Re: Everything I wish I knew when learning C

#116
post #79

Earlier quoted context omitted.

Was rewriting the stack due to undefined behavior or was it due to a logic error, e.g. improper bounds calculation?

Isn’t all UB a result of logic errors? Writing beyond the end of allocated memory (due to incorrect bounds calculation ) is an example of undefined behaviour

No, even type-punning properly allocated memory (e.g. using memory to reinterpret the bits of a floating point number as an integer) through pointers is UB because compilers want to use types for alias analysis[1]. In order to do that "properly" you are supposed to use a union. In C++ you are supposed to use the reinterpret_cast operator.

[1] Which IMO goes back to C's original confusion of mixing up machine-level concepts with language-level concepts from the get-go, leaving optimizers no choice but unsound reasoning and blaming programmers when they get it wrong. Something something numerical loops and supercomputers.

Re: Everything I wish I knew when learning C

#117
post #107
post #6

Some constructive feedback: > Here are the absolute essential flags you may need. I highly recommend including `-fsanitize=address,undefined` in there (docs: https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.h... ). (Edit: But probably not in release builds, as @rmind points out.) > The closest thing to a convention I know of is that some people name types like my_type_t since many standard C types are like…

UPDATE regarding "_t" suffix: POSIX reserves "_t" suffix everywhere (not just for identifiers beginning with "int"/"uint" from ); references: https://www.gnu.org/software/libc/manual/html_node/Reserved-... , https://pubs.opengroup.org/onlinepubs/9699919799/functions/V... . So I actually stand by my original comment that the convention of using "_t" suffix shouldn't be recommended. (It's just that the reasoning is for…

Well, semantically, "size_t" makes sense to me ("the type of a size variable"), while "uint_t" does not ("the type of a uint variable"), because "uint" is already a type, obviously - just like "int".

Re: Everything I wish I knew when learning C

#118
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 language.

(4) so?? I would think I would be more concerned about how to use 'c' for my problem, without worrying about how to use 'c' to solve some other problem. It is hard for me to understand why a language that can do many things is more problematic than a language that only does a few things.

My sense is that reading this article makes things harder, not easier. Most people do not care whether an int is 32 or 64 bits.

I won't argue that copying things (that are not ints or floats) needs to be understood, but many popular languages (specifically python) have the same problem. Understanding the difference between a reference and a value is important for most languages.

There are different schools of thought -- those that can imagine lots of issues after reading the documentation, vs those that simply try writing code and start exploring edge cases when something breaks. I learn faster by trying things, and rarely encounter the edge-case issues.

Re: Everything I wish I knew when learning C

#119
post #100

Earlier quoted context omitted.

Good one, thanks. Note that it requires Clang; GCC 12.2 doesn't have it.

Wow nice, I didn't know about this one. I can add some more which are less known. This is my current sanitize invocation (minus the addition of "integer" which I'll be adding, unless one of these other ones covers it): -fsanitize=address,leak,undefined,cfi,function CFI has checks for unrelated casts and mismatched vtables which is very useful. It requires that you pass -flto or -flto=thin and -fvisibility=hidden. You…

I think "leak" is always enabled by "address". It's only useful if you want run LeakSanitizer in stand-alone mode. "integer" is only enabled on demand because it warns about well-defined (but still dangerous) code. You can also enable "unsigned-integer-overflow" and "implicit-conversion" separately. See https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html#...

Re: Everything I wish I knew when learning C

#120
post #64

The elderly "C Traps and Pitfalls" http://www.literateprogramming.com/ctraps.pdf was an extremely helpful book when I was learning C, although I don't know how well it's held up.

I skimmed it and all the points made still appear to be true today. Namely, all the C language rules and the human psychological pitfalls.
Post reply on HN