in the declaration.
C99 allows you to declare variables anywhere in a function. Declare them in as
local a scope as possible, and as close to the first use as possible. This
makes it easier for the reader to find the declaration and see what type the
variable is and what it was initialized to. In particular, initialization
should be used instead of declaration and assignment, e.g. >c
int i;
i = f(); // ❌: initialization separate from declaration.
int j = g(); // ✅: declaration has initialization.
Initialization ~
Multiple declarations can be defined in one line if they aren't initialized,
but each initialization should be done on a separate line.
>c
int i;
int j; // ✅
int i, j; // ✅: multiple declarations, no initialization.
int i = 0;
int j = 0; // ✅: one initialization per line.
int i = 0, j; // ❌: multiple declarations with initialization.
int i = 0, j = 0; // ❌: multiple declarations with initialization.
==============================================================================
Nvim-Specific Magic
clint ~
Use `clint.py` to detect style errors.
`src/clint.py` is a Python script that reads a source file and identifies
style errors. It is not perfect, and has both false positives and false
negatives, but it is still a valuable tool. False positives can be ignored by
putting `// NOLINT` at the end of the line.
uncrustify ~
src/uncrustify.cfg is the authority for expected code formatting, for cases
not covered by clint.py. We remove checks in clint.py if they are covered by
uncrustify rules.
==============================================================================
Other C Features *dev-style-features*
Variable-Length Arrays and alloca() ~
We do not allow variable-length arrays or `alloca()`.
Variable-length arrays can cause hard to detect stack overflows.
Postincrement and Postdecrement ~
Use postfix form (`i++`) in statements. >c
for (int i = 0; i < 3; i++) { }
int j = ++i; // ✅: ++i is used as an expression.
for (int i = 0; i < 3; ++i) { }
++i; // ❌: ++i is used as a statement.
Use of const ~
Use `const` pointers whenever possible. Avoid `const` on non-pointer parameter definitions.
Where to put the const ~
Some people favor the form `int const *foo` to `const int *foo` . They
argue that this is more readable because it's more consistent: it keeps
the rule that `const` always follows the object it's describing. However,
this consistency argument doesn't apply in codebases with few
deeply-nested pointer expressions since most `const` expressions have only
one `const`, and it applies to the underlying value. In such cases, there's
no consistency to maintain. Putting the `const` first is arguably more
readable, since it follows English in putting the "adjective" (`const`)
before the "noun" (`int`).
That said, while we encourage putting `const` first, we do not require it.
But be consistent with the code around you! >c
void foo(const char *p, int i);
}
int foo(const int a, const bool b) {
}
int foo(int *const p) {
}
Integer Types ~
Of the built-in integer types only use `char`, `int`, `uint8_t`, `int8_t`,
`uint16_t`, `int16_t`, `uint32_t`, `int32_t`, `uint64_t`, `int64_t`,
`uintmax_t`, `intmax_t`, `size_t`, `ssize_t`, `uintptr_t`, `intptr_t`, and
`ptrdiff_t`.
Use `int` for error codes and local, trivial variables only.
Use care when converting integer types. Integer conversions and promotions can
cause non-intuitive behavior. Note that the signedness of `char` is
implementation defined.
Public facing types must have fixed width (`uint8_t`, etc.)
There are no convenient `printf` format placeholders for fixed width types.
Cast to `uintmax_t` or `intmax_t` if you have to format fixed width integers.
Type unsigned signed
`char` `%hhu` `%hhd`
`int` n/a `%d`
`(u)intmax_t`