Outdated. Need to rewirte
C
tips
** Warnings**
โโโโ#include <stdint.h>
** Special Standard Types**
** One Exception to never-char**
The only acceptable use of char in 2016 is if a pre-existing API requires char (e.g. strncat, printf'ing "%s", โฆ) or if you're initializing a read-only string
โโโโ (e.g. const char *hello = "hello";) because the C type of string literals ("hello") is char[].
ALSO: In C11 we have native unicode support, and the type of UTF-8 string literals is still char
โโโโ[] even for multibyte sequences like const
โโโโchar *abcgrr = u8"abc?";
The correct type for pointer math is uintptr_t defined by <stdint.h>, while the also useful ptrdiff_t is defined by stddef.h.
Instead of:
โโโโlong diff = (long)ptrOld - (long)ptrNew;
Use:
โโโโptrdiff_t diff = (uintptr_t)ptrOld - (uintptr_t)ptrNew;
** System-Dependent Types **
If we skip over the line of thinking where you are deliberately introducing difficult to reason about code by using two different sizes depending on platform, you still don't want to use long for system-dependent types.
In these situations, you should use intptr_t โ the integer type capable of holding a pointer value for your platform.
For holding pointer offsets, we have the aptly named ptrdiff_t which is the proper type for storing values of subtracted pointers.
** Maximum Value Holders **
The safest container for any integer is intmax_t (also uintmax_t). You can assign or cast any signed integer tointmax_t with no loss of precision, and you can assign or cast any unsigned integer to uintmax_t with no loss of precision.
** That Other Type **
Other uses include: size_t is the type of the argument to malloc, and ssize_t is the return type of read() andwrite() (except on Windows where ssize_t doesn't exist and the return values are just int).
Printing Types
One note about the PRI* formatting specifiers: they are macros and the macros expand to proper printf type specifiers on a platform-specific basis. This means you can't do:
โโโโprintf("Local number: %PRIdPTR\n\n", someIntPtr);
but instead, because they are macros, you do:
โโโโprintf("Local number: %" PRIdPTR "\n\n", someIntPtr);
Notice you put the % inside your format string literal, but the type specifier is outside your format string literal because all adjacent strings get concatentated by the preprocessor into one final combined string literal.
** C99 allows variable declarations anywhere**
if you have tight loops, test the placement of your initializers. Sometimes scattered declarations can cause unexpected slowdowns.
So, do NOT do this:
โโโโvoid test(uint8_t input) {
โโโโ uint32_t b;
โโโโ if (input > 3) {return;}
โโโโ b = input;
โโโโ}
do THIS instead:
โโโโvoid test(uint8_t input) {
โโโโif (input > 3) {return;}
โโโโ uint32_t b = input;
โโโโ}
** Modern compilers support **#pragma** **once
So, do NOT do this:
โโโโ#ifndef PROJECT_HEADERNAME#define PROJECT_HEADERNAME...#endif /* PROJECT_HEADERNAME */
Do THIS instead:
โโโโ#pragma once
โโโโ#pragma
โโโโonce tells the compiler to only include your header once and you do not need three lines of header guards anymore. This pragma is widely supported across all compilers across all platforms and is recommended over manually naming header guards.
For more details, see list of supported compilers at pragma once.
So, do NOT do this:
โโโโ uint32_t numbers[64]; memset(numbers, 0, sizeof(numbers));
Do THIS instead:
โโโโ uint32_t numbers[64] = {0};
So, do NOT do this:
โโโโ struct thing {uint64_t index;uint32_t counter;};
โโโโ struct thing localThing;
โโโโ void initThing(void) {memset(&localThing, 0, sizeof(localThing));}
Do THIS instead:
โโโโ struct thing {uint64_t index;uint32_t counter;};
โโโโ struct thing localThing = {0};
IMPORTANT NOTE: If your struct has padding, the {0} method does not zero out extra padding bytes. For example, struct thing has 4 bytes of padding after counter (on a 64-bit platform) because structs are padded to word-sized increments. If you need to zero out an entire struct including unused padding, usememset(&localThing,0, sizeof(localThing)) because sizeof(localThing)== 16 bytes even though the addressable contents is only 8
If you need to re-initialize already allocated structs, declare a global zero-struct for later assignment:
โโโโstruct thing {uint64_t index;uint32_t counter;};
โโโโstatic const struct thing localThingNull = {0};
โโโโstruct thing localThing = {.counter = 3};
โโโโlocalThing = localThingNull;
If you are lucky enough to be in a C99 (or newer) environment, you can use compound literals instead of keeping a global "zero struct" around (also see, from 2001, The New C: Compound Literals).
Compound literals allow your compiler to automatically create temporary anyonomous structs then copy them onto a target value:
โโโโ localThing = (struct thing){0};
So, do NOT do this (if you know your array is tiny or you are just testing something quickly):
โโโโ uintmax_t arrayLength = strtoumax(argv[1], NULL, 10);
โโโโ void *array[];
โโโโ array = malloc(sizeof(*array) * arrayLength);
โโโโ /* remember to free(array) when you're done using it */
Do THIS instead:
โโโโ uintmax_t arrayLength = strtoumax(argv[1], NULL, 10);
โโโโ void *array[arrayLength];
โโโโ /* no need to free array */
See the restrict keyword (often __restrict)
If a function accepts arbitrary input data and a length to process, don't restrict the type of the parameter.
So, do NOT do this:
โโโโvoid processAddBytesOverflow(uint8_t *bytes, uint32_t len)
โโโโ{
โโโโfor (uint32_t i = 0; i < len; i++) {
โโโโ bytes[0] += bytes[i];
โโโโ }
โโโโ}
Do THIS instead:
โโโโvoid processAddBytesOverflow(void *input, uint32_t len) {
โโโโ uint8_t *bytes = input;
โโโโ for (uint32_t i = 0; i < len; i++) {
โโโโ bytes[0] += bytes[i];
โโโโ }
โโโโ}
C99 gives us the power of <stdbool.h> which defines true to 1 and false to 0.
So, do NOT do this:
โโโโvoid *growthOptional(void *grow, size_t currentLen, size_t newLen) {
โโโโ if (newLen > currentLen) {
โโโโ void *newGrow = realloc(grow, newLen);
โโโโ if (newGrow) {/* resize success */grow = newGrow;}
โโโโ else {
โโโโ /* resize failed, free existing and signal failure through NULL */ free(grow);
โโโโ grow = NULL;
โโโโ }
โโโโ}
โโโโ return grow;
โโโโ}
Do THIS instead:
โโโโ/* Return value: * - 'true' if newLen > currentLen and attempted to grow * - 'true' does not signify success here, the success is still in '*_grow' * - 'false' if newLen <= currentLen */
โโโโbool growthOptional(void **_grow, size_t currentLen, size_t newLen) {
โโโโ void *grow = *_grow;
โโโโ if (newLen > currentLen) {
โโโโ void *newGrow = realloc(grow, newLen);
โโโโ if (newGrow) {
โโโโ /* resize success */
โโโโ *_grow = newGrow;
โโโโ return true;
โโโโ }
โโโโ /* resize failure */
โโโโ free(grow);
โโโโ *_grow = NULL;
โโโโ /* for this function, * 'true' doesn't mean success, it means 'attempted grow' */
โโโโ return true;
โโโโ }
โโโโ return false;
โโโโ }
Or, even better, Do THIS instead:
โโโโtypedef enum growthResult {
โโโโGROWTH_RESULT_SUCCESS = 1,
โโโโGROWTH_RESULT_FAILURE_GROW_NOT_NECESSARY,
โโโโGROWTH_RESULT_FAILURE_ALLOCATION_FAILED
โโโโ} growthResult;
โโโโ
โโโโgrowthResult growthOptional(void **_grow, size_t currentLen, size_t newLen) {
โโโโ void *grow = *_grow;
โโโโ if (newLen > currentLen) {
โโโโ void *newGrow = realloc(grow, newLen);
โโโโ if (newGrow) {
โโโโ /* resize success */
โโโโ *_grow = newGrow;
โโโโ return GROWTH_RESULT_SUCCESS;
โโโโ }
โโโโ /* resize failure, don't remove data because we can signal error */ return GROWTH_RESULT_FAILURE_ALLOCATION_FAILED;
โโโโ }
โโโโ return GROWTH_RESULT_FAILURE_GROW_NOT_NECESSARY;
โโโโ }
The only usable C formatter as of 2016 is clang-format. clang-format has the best defaults of any automatic C formatter and is still actively developed.
clang-format:
โโโโclang-format -help
An easy way to create the .clang-format file is:
โโโโclang-format -style=llvm -dump-config > .clang-format
There is an integration for vim which lets you run the clang-format standalone tool on your current buffer, optionally selecting regions to reformat. The integration has the form of a python-file which can be found under clang/tools/clang-format/clang-format.py.
This can be integrated by adding the following to your .vimrc:
โโโโmap <C-K> :pyf <path-to-this-file>/clang-format.py<cr>
โโโโimap <C-K> <c-o>:pyf <path-to-this-file>/clang-format.py<cr>
Script for patch reformatting
โโโโusage: clang-format-diff.py [-h] [-i] [-p NUM] [-regex PATTERN] [-style STYLE]
So to reformat all the lines in the latest git commit, just do:
โโโโgit diff -U0 HEAD^ | clang-format-diff.py -i -p1
You should always use calloc. There is no performance penalty for getting zero'd memory. If you don't like the function protype of calloc(objectcount, size per object) you can wrap it with
โโโโ#define mycalloc(N) calloc(1, N)
Never memset(ptr,0, len) when you can statically initialize a structure (or array) to zero (or reset it back to zero by assigning from an in-line compound literal or by assigning from a global zero'd out structure).
Though, memset() is your only choice if you need to zero out a struct including its padding bytes (because {0}only sets defined fields, not undefined offsets filled by padding).