5 ms·
While I don't agree every single point (see below), one thing great about this document is that the author tried to really elaborate one's opinion. That makes a
by lifthrasiir 2y ago
While I don't agree every single point (see below), one thing great about this document is that the author tried to really elaborate one's opinion. That makes a good point to start the discussion regardless of my own opinion. Thus I'll contribute back by giving my own judgement for every single item here:
Absolute agreement
* Always develop and compile with all warnings (and more) on
* #include the definition of everything you use
* Provide include guards for all headers to prevent double inclusion
* Always comment `#endif`s of large conditional sections
* Declare variables as late as possible
* Be consistent in your variable names across functions
* Minimize the scope of variables
* Use `assert` everywhere your program would fail otherwise
* Repeat `assert` calls; don't `&&` them together
* C isn't object-oriented, and you shouldn't pretend it is
Strong agreement with some obvious exceptions
* Use `//` comments everywhere, never `/* ... */`
* Comment non-standard-library `#include`s to say what symbols you use from them
* No global or static variables if you can help it (you probably can)
* Minimize what you expose; declare top-level names static where you can
* Use `double` rather than `float`, unless you have a specific reason otherwise
* Avoid non-pure or non-trivial function calls in expressions
* Simple constant expressions can be easier to read than variables
* Initialize strings as arrays, and use sizeof for byte size
* Where possible, use `sizeof` on the variable; not the type
* Document your struct invariants, and provide invariant checkers
* Avoid `void *` because it harms type safety
* If you have a `void *`, assign it to a typed variable as soon as possible
* Only use pointers in structs for nullity, dynamic arrays or incomplete types
* Avoid getters and setters
Agreed but you need a few more words
* Don't be afraid of short variable names [if the scope fits on a screen]
* Explicitly compare values; don't rely on truthiness
[unless values themselves are boolean]
* Use parentheses for expressions where the operator precedence isn't obvious
[but `&foo->bar` *is* obvious]
* Separate functions and struct definitions with two lines
[can use comments instead]
* If a macro is specific to a function, `#define` it in the body [and `#undef` ASAP]
* Only typedef structs; never basic types or pointers
[or make them distinct enough, but ISO C stole a `_t` suffix]
I do so or I see why but that's really a problem of C and its ecosystem instead
* Use GCC's and Clang's `-M` to automatically generate object file dependencies
* Avoid unified headers
* Immutability saves lives: use `const` everywhere you can
* Use `bool` from `stdbool.h` whenever you have a boolean value
* Avoid unsigned types because the integer conversion rules are complicated
* Prefer compound literals to superfluous variables
* Never use array syntax for function arguments definitions
* Don't use variable-length arrays
* Use C11's anonymous structs and unions rather mutually-exclusive fields
* Give structs TitleCase names, and typedef them
* Never begin names with `_` or end them with `_t`: they're reserved for standards
* Only use pointer arguments for nullity, arrays or modifications
* Prefer to return a value rather than modifying pointers
* Always use designated initializers in struct literals
I do so but am not sure
* Write to the most modern standard you can [we have no choice for many cases]
* Program in American English [only applicable for native speakers]
I see why but I think you are mislead
* Don't write argument names in function prototypes if they just repeat the type
[such case is very, very rare]
* Use `+= 1` and `-= 1` over `++` and `--`
[`++`/`--` should be read as succ/pred and should be exclusively used for pointers]
* Don't use `switch`, and avoid complicated conditionals
[switch is okay once you have enabled enough warnings]
* Only upper-case a macro if will act differently than a function call
[agreed in principle, but should define "differently" more broadly]
* Always prefer array indexing over pointer arithmetic
[and then you will be biten by index variable types, remember `ptrdiff_t`]
That's really just a personal preference
* We can't get tabs right, so use spaces everywhere
[as long as mechanically enforcable, the choice itself is irrelevant]
* Always put `const` on the right and read types right-to-left [too eyesore]
* Use one line per variable definition; don't bunch same types together
[will agree with some significant exceptions though]
* Never change state within an expression (e.g. with assignments or `++`)
[absolutely avoid functions, but `++` has its uses]
* Always use brackets, even for single-statement block
[rather a read-write trade-off; this may make some codes harder to read]
* Never use or provide macros that wrap control structures like `for`
[the example is very tame in comparison to actually problematic macros]
* Don't typecast unless you have to (you probably don't)
[while many typecasts can be easily removed, excess doesn't do actual harm]
* Give enums `UPPERCASE_SNAKE` names, and lowercase their values
[I would rather avoid enums for various reasons]
* Use structs to name functions' optional arguments
[maybe the author tried to say "avoid too many arguments" instead?]
* If you're providing allocation and free functions only for a struct member,
allocate memory for the whole struct
[that complicates using struct as a value]
Just no.
* Never have more than 79 characters per line
[100 or 120 do work equally well, you do need some limit though]
* Define a constant for the size of every enum
[would imply that all enum values are sequential, and that's not true!]
- rramadass 2y agoNice Summary of the Article ! Thank You.
- jstimpfle 2y ago> * We can't get tabs right, so use spaces everywhere > [as long as mechanically enforcable, the choice itself is irrelevant] It's not mechanically enforceable (in practice), that's the point. Forbidding tabs altogether is the most practical and actionable path.
- lifthrasiir 2y agoI'm not sure what you have in mind, but in my mind the mechanical enforcement really means something like clang-format [1] and that surely works. [1] https://clang.llvm.org/docs/ClangFormatStyleOptions.html#usetab https://clang.llvm.org/docs/ClangFormatStyleOptions.html#use...
- jstimpfle 2y agoHave you actually tried? Personally I have evaluated and then abandonded clang-format (for reasons including but not limited to the interplay between macros and indentation), and most tool's support for tab-indentation + space-alignments is flaky to non-existent. I wouldn't want to constrain my setup to one that integrates clang-format just for a needlessly complicated requirement when I could just abandon tabs altogether.
- lifthrasiir 2y agoIf you want to keep space alignments that's honestly to be expected. Exclusively using tabs would work much better, partly for the reason you have said. On the requirement of clang-format itself though... Yeah, that is more like a problem of C and its ecosystem indeed.
- janice1999 2y ago> * No global or static variables if you can help it (you probably can) I can't imagine doing this on embedded systems.