If possible, use the assignment operator instead of memcpy
:
struct s *s1;
struct s *s2;
*s1 = *s2;
- it's typesafe and you'll get an error if the types of the two variables don't match
- you can't accidentally copy the wrong amount of data
- can't be used on un-aligned data
- can't be used if you expect data after the struct (e.g. through a zero-sized array)
- Usually equivalent, in some cases either slower or faster depending on the compiler
If possible, use struct assignments to initialize them instead of initializing them one by one.
*s = (struct s){
.field1 = 5,
.field2 = 6,
};
struct s init_value = {
.field1 = 5,
.field2 = 6,
};
*s = s;
- All fields are guaranteed to be initialized, missing ones will be zeroed
- less copy-pasted and more readable code since you don't have to repeat the variable name for every field
Use a zero-assignment instead of memset
*s1 = (struct s){ 0 };
struct s init_value = { 0 };
*s1 = init_value;
- you can't make mistakes when specifying the size
If possible, use static assertions to express safety-related assumptions.
static uint8_t buf[CONFIG_BUF_SIZE];
BUILD_ASSERT(sizeof(buf) >= 4);
void my_code(void) {
buf[3] = 42;
}
- enforces that your assumptions are correct
- documents which assumptions you made
The best one I've seen so far is the one used in Zephyr RTOS
#define _DO_CONCAT(x, y) x ## y
#define _CONCAT(x, y) _DO_CONCAT(x, y)
#define BUILD_ASSERT(EXPR, MSG...) \
enum _CONCAT(__build_assert_enum, __COUNTER__) { \
_CONCAT(__build_assert, __COUNTER__) = 1 / !!(EXPR) \
}
void mycode(void) {}
- Without the
void
you can pass any or no arguments to that function without ever getting any warnings - if the header uses
()
and the implementation uses(int a)
you also don't get warnings which causes unsafe behavior