Live data from Hacker News

C Style: My favorite C programming practices (2014)

github.com

101–110 of 139 posts

Re: C Style: My favorite C programming practices (2014)

#101

> 80-characters-per-line is a de-facto standard for viewing code. Readers of your code who rely on that standard, and have their terminal or editor sized to 80 characters wide, can fit more on the screen by placing windows side-by-side. This is one of the silliest practices to still be enforced or even considered in 2024. “Readers” should get a modern IDE/text editor and/or modern hardware.

IMHO if the 80-column limit bothers you in C, you're writing bad C. Quoting the kernel docs, it is "warning you when you’re nesting your functions too deep. Heed that warning".

I remember reading this for the first time as a teenager: "if you need more than 3 levels of indentation, you’re screwed anyway, and should fix your program". Twenty years later, it seems like solid advice to me.

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin...

Re: C Style: My favorite C programming practices (2014)

#102
Tabs vs Spaces

Tabs are always correct, IF spaces are never used instead. One tab, for one level of indent. Adjust to preference.

Alas, I don't think there's a standard way of specifying...

// kate: space-indent off; indent-width 8; tab-width 8; mixedindent off; indent-mode tab;

Similarly, // comments should be preferred, but /* comments */ are acceptable at the top of large function blocks for large blobs of comments. Judicious / sparing use as the key idea to make it worth the exceptions if commenting out large blocks during tests or refactors.

Re: C Style: My favorite C programming practices (2014)

#103

I agree with most, and most of the others I might quibble with, but accept. However, the item to not use unsigned types is vastly stupid! Signed types have far more instances of UB, and in the face of 00UB [1], that is untenable. It is correct that mixing signed and unsigned is really bad; don't do this. Instead, use unsigned types for everything, including signed math. Yes, you can simulate two's complement with uns…

In the vast majority of cases, integer overflow or truncation when casting is a bug, regardless whether it is undefined, implementation-defined or well-defined behavior. Avoiding undefined behavior doesn't buy you anything. If you start to fuzz test with UBSan and -fsanitize=integer, you will realize that the choice of integer types doesn't matter much. Unsigned types have the benefit that overflowing the left end of…

> In the vast majority of cases, integer overflow or truncation when casting is a bug, regardless whether it is undefined, implementation-defined or well-defined behavior. Avoiding undefined behavior doesn't buy you anything.

With respect, this is nonsense. With UB, the compiler might remove the line of code entirely. With overflow/underflow/truncation, the results are well-defined and the compiler is not allowed to simply remove the offending line.

Re: C Style: My favorite C programming practices (2014)

#104

I agree with most, and most of the others I might quibble with, but accept. However, the item to not use unsigned types is vastly stupid! Signed types have far more instances of UB, and in the face of 00UB [1], that is untenable. It is correct that mixing signed and unsigned is really bad; don't do this. Instead, use unsigned types for everything, including signed math. Yes, you can simulate two's complement with uns…

Author here, 10 years later -- I agree. I'd remove that rule wholesale in an update of this guide. Unsigned integer types can and should be used, especially for memory sizes. I would still advocate for large signed types over unsigned types for most domain-level measurements . Even if you think you "can't" have a negative balance or distance field, use a signed integer type so that underflows are more correct. Althou…

I think with those changes, my disagreement would become a mere quibble.

> I would still advocate for large signed types over unsigned types for most domain-level measurements. Even if you think you "can't" have a negative balance or distance field, use a signed integer type so that underflows are more correct.

I agree with this, but I think I would personally still use unsigned types simulating two's complement that gives the correct underflow semantics. Yeah, I'm a hard egg.

Re: C Style: My favorite C programming practices (2014)

#105

Earlier quoted context omitted.

I'm torn both ways on the double issue. On the one hand, doubles are much more widely supported these days, and will save you from some common scenarios. Timestamps are a particular one, where a float will often degrade on a time scale that you care about, and doubles not. A double will also hold any int value without loss (on mainstream platforms), and has enough precision to allow staying in world coordinates for 3…

Are there C++ libs that use floating points for timestamps? I was under the impression that most stacks have accepted int64 epoch microseconds as the most reasonable format.

Don't have a publicly visible reference to give at the moment, but it's still sometimes seen where relative timestamps are being tracked, such as in an audio library tracking time elapsed since start. It's probably less used for absolute time where the precision problems are more obvious.

Re: C Style: My favorite C programming practices (2014)

#106
post #2

I feel like I probably agree with about 80% of this. It also seems like this would apply fairly well to C++ as well. One thing that I'll strongly quibble with: "Use double rather than float, unless you have a specific reason otherwise". As a graphics programmer, I've found that single precision will do just fine in the vast majority of cases. I've also found that it's often better to try to make my code work well in…

I think your case comes under the "specific reason to use `float`"? If I am writing some code and I need floating point numbers, then without any more context, I will choose `double`. If I have context and the context makes it so `float`s are vastly better, then I will use `float`s.

Re: C Style: My favorite C programming practices (2014)

#107

Earlier quoted context omitted.

I'm torn both ways on the double issue. On the one hand, doubles are much more widely supported these days, and will save you from some common scenarios. Timestamps are a particular one, where a float will often degrade on a time scale that you care about, and doubles not. A double will also hold any int value without loss (on mainstream platforms), and has enough precision to allow staying in world coordinates for 3…

Are there C++ libs that use floating points for timestamps? I was under the impression that most stacks have accepted int64 epoch microseconds as the most reasonable format.

It's very common in games.

Integers are always an option, of course, but in this context it's hard to beat the convenience of just storing seconds in a floating point number.

Related: https://randomascii.wordpress.com/2012/02/13/dont-store-that...

Re: C Style: My favorite C programming practices (2014)

#108
post #5

> Write correct, readable, simple and maintainable software, and tune it when you're done, with benchmarks to identify the choke points If speed is a primary concern, you can't tack it on at the end, it needs to be built in architecturally. Benchmarks applied after meeting goals of read/maintainability are only benchmarking the limits of that approach and focus. They can't capture the results of trying and benchmarki…

Well, yes. Architect for performance, try not to do anything "dumb", but save micro-optimizations for after performance measurement.

The problem with all of these rules of thumb is that they're vague to the point of being vacuously true. Of course we all agree that "premature optimization is the root of all evil" as Knuth once said, but the saying itself is basically a tautology: if something is "premature", that already means it's wrong to do it.

I'll be more impressed when I see specific advice about what kinds of "optimizations" are premature. Or, to address your reply specifically, what counts as "doing something dumb" vs. what is a "micro-optimization". And, the truth is, you can't really answer those questions without a specific project and programming language in mind.

But, what I do end up seeing across domains and programming languages is that people sacrifice efficiency (which is objective and measurable, even if "micro") for a vague idea of what they consider to be "readable" (today--ask them again in six months). What I'm specifically thinking of is people writing in programming languages with eager collection types that have `map`, `filter`, etc methods, and they'll chain four or five of them together because it's "more readable" than a for-loop. The difference in readability is absolutely negligible to any programmer, but they choose to make four extra heap-allocated, temporary, arrays/lists and iterate over the N elements four or five times instead of once because it looks slightly more elegant (and I agree that it does). Is it a "micro-optimization" to just opt for the for-loop so that I don't have to benchmark how shitty the performance is in the future when we're iterating over more elements than we thought we'd ever need to? Or is it not doing something dumb? To me, it seems ridiculous to intentionally choose a sub-optimal solution when the optimal one is just as easy to write and 99% (or more) as easy to read/understand.

Post reply on HN