Live data from Hacker News

My personal C coding style as of late 2023

nullprogram.com

51–60 of 466 posts

Re: My personal C coding style as of late 2023

#51
While there are a few disagreeable points, I like the article.

I've always felt that C is unfairly maligned. Yes, it's very low level, it's meant to be. Yes, it lets you shoot yourself in the foot, but what language doesn't?

Most of the problems with C are really issues with the standard library, the Unix (now Posix) interfaces, and the string type.

None of these are actually part of C, but are part of how C is normally used. So those problems can be avoided, and use C for what it's good at.

Re: My personal C coding style as of late 2023

#52

I might just be a grumpy old dev but a lot of this stuff gets an immediate no from me because it’s so unidiomatic. You have to unlearn the accepted way of doing things and you end up with a codebase that is just so foreign to anyone looking at even a small chunk of it, unless they are committed to really learning to do things your way. Everyone knows what a uint32_t is when they see it. The cognitive overhead (until…

> I might just be a grumpy old dev [...] Everyone knows what a uint32_t is when they see it.

You might not be old enough then :-P many codebases typedef their own int types. See glib (gint, gshort, gint32, etc), SDL (Sint32, Uint32, etc) off the top of my head and there are many that define types like "int32" or "i32" like the linked article.

Re: My personal C coding style as of late 2023

#53

> #define sizeof(x) (size)sizeof(x) Technically, it's illegal to #define over a language keyword.

If you're dead set on doing this, the correct way would be to name the macro in all caps e.g. #define SIZEOF(x) as C is case sensitive. It is somewhat self-documenting to the next guy that SIZEOF() != sizeof().

Re: My personal C coding style as of late 2023

#55
post #22
post #15

> #define assert(c) while (!(c)) __builtin_unreachable() This seems like a bad idea, because the whole point of an assert is that something shouldn't happen, but might due to a (future?) bug.

> This seems like a bad idea, because the whole point of an assert is that something shouldn't happen, but might due to a (future?) bug. And so it’s a bad idea because…? The whole idea is to notice a bug before it ships. Asserts are usually enabled in test and debug builds. So having an assert hit the “unreachable” path should be a good way to notice “hey, you’ve achieved the unexpected” in a bad way. You’re going to…

It's undefined behavior if the assert triggers in production. It's too greedy for minor performance benefit at the risk of causing strange issues.

Re: My personal C coding style as of late 2023

#56
post #51

While there are a few disagreeable points, I like the article. I've always felt that C is unfairly maligned. Yes, it's very low level, it's meant to be. Yes, it lets you shoot yourself in the foot, but what language doesn't? Most of the problems with C are really issues with the standard library, the Unix (now Posix) interfaces, and the string type. None of these are actually part of C, but are part of how C is norma…

> I've always felt that C is unfairly maligned. Yes, it's very low level, it's meant to be. Yes, it lets you shoot yourself in the foot, but what language doesn't

Isn’t it a beauty of lower level languages that creating higher level abstractions provides more value?

edit: typo

Re: My personal C coding style as of late 2023

#57
post #33

> #define sizeof(x) (size)sizeof(x) I'm guessing this is lacking an outer pair of parentheses (i.e. it's not `((size)sizeof(x))`) on the grounds that they're unnecessary. In terms of operator precedence, casting binds tightly, so if you write e.g. `sizeof(x) * 3`, it expands to `(size)sizeof(x) * 3`, which is equivalent to `((size)sizeof(x)) * 3`: the cast happens before the multiplication. Indeed, casting binds more…

(self-reply) One more thing.

> I could use _Bool, but I’d rather stick to a natural word size and stay away from its weird semantics.

This is even more subjective, but personally I like _Bool's semantics. They mean that if an expression works in an `if` statement:

    if (flags & FLAG_ALLOCATED)
then you can extract that same expression into a boolean variable:

    _Bool need_free = flags & FLAG_ALLOCATED;
The issue is that `flags & FLAG_ALLOCATED` doesn't equal '0 if unset, 1 if set', but '0 if unset, some arbitrary nonzero value if set'. (Specifically it equals FLAG_ALLOCATED if set, which might be 1 by coincidence, but usually isn't.) This kind of punning is fine in an `if` statement, since any nonzero value will make the check pass. And it's fine as written with `_Bool`, since any nonzero integer will be converted to 1 when the expression is implicitly converted to `_Bool`. But if you replace `_Bool` with `int`, then this neither-0-nor-1 value will just stick around in the variable. Which can cause strange consequences. It means that

    if (need_free)
will pass, but

    if (need_free == true)
will fail. And if you have another pseudo-bool, then

    if (need_free == some_other_bool)
might fail even if both variables are considered 'true' (i.e. nonzero), if they happen to have different values.

_Bool solves this problem. Admittedly, the implicitness has downsides. If you're refactoring the code and you decide you don't really need a separate variable, you might try to replace all uses of `need_free` with its definition, not realizing that the implicit conversion to _Bool was doing useful work. So you might end up with incorrect code like:

    if ((flags & FLAG_ALLOCATED) == true)
Also, if you are reading a struct from disk or otherwise stuffing it with arbitrary bytes, and the struct has a _Bool, then you risk undefined behavior if the corresponding byte becomes something other than 0 or 1 – because the compiler assumes that the implicit conversion to 0 or 1 has been done already.

Re: My personal C coding style as of late 2023

#58
post #51

While there are a few disagreeable points, I like the article. I've always felt that C is unfairly maligned. Yes, it's very low level, it's meant to be. Yes, it lets you shoot yourself in the foot, but what language doesn't? Most of the problems with C are really issues with the standard library, the Unix (now Posix) interfaces, and the string type. None of these are actually part of C, but are part of how C is norma…

> Yes, it lets you shoot yourself in the foot, but what language doesn't?

Good lord.

Re: My personal C coding style as of late 2023

#60
post #43

> #define sizeof(x) (size)sizeof(x) Undefined behavior[1] > #define assert(c) while (!(c)) __builtin_unreachable() Undefined behavior[1] > I’ll cast away the const if needed. Undefined behavior[2] > The assignments are separated by sequence points, giving them an explicit order. I don't believe assignments are sequence points and only the function call is. [1] https://en.cppreference.com/w/c/language/identifier#Reser…

> > I’ll cast away the const if needed.

> Undefined behavior[2]

To be clear, it's only UB if the object was defined const, which is the case given he wrote:

> One small exception: I still like it as a hint to place static tables in read-only memory closer to the code. I’ll cast away the const if needed.

So you are correct on this point. Funnily enough, such objects are relatively rare IME, so I had to double-check to see that he was advocating it specifically in the rare case where it must not be applied.

Post reply on HN