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).