A function’s return value and parameters may be decorated with attributes to convey additional information to the compiler and/or other
developers.
A commonly used attribute is nonnull
which can be used to mark a function’s return value and parameters as shown in the following:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
__attribute__((returns_nonnull)) int *
make_array_copy(__attribute__((nonnull)) int *src, size_t len) {
int *dst = (int *)malloc(len * sizeof(int));
if (dst == NULL) {
perror("malloc failed");
exit(1);
}
memcpy(dst, src, len);
return dst;
}
The nonnull
attribute is meant for other developers and as a hint for compilers. Values marked as nonnull
are assumed to
have non-null values.
However, developers may accidentally break the nonnull
attribute as shown in the following code snippet:
__attribute__((returns_nonnull))
int* foo(__attribute__((nonnull)) int* x) {
x = 0; // Noncompliant: `x` is marked "nonnull" but is set to null
foo(0); // Noncompliant: null is passed as an argument marked as "nonnull"
return 0; // Noncompliant: return value is marked "nonnull" but null is returned
}
Failing to adhere to the attribute may introduce serious program errors. In particular, the compiler does not enforce that values marked as
nonnull
are indeed non-null at runtime; it is the developers' responsibility to adhere to the attribute. These values are typically
not null-checked before use. Setting a value marked as nonnull
to null (i.e., NULL
, 0
or
nullptr
) is hence likely to cause a null-pointer dereference. Compilers may even apply optimizations based on this attribute and might,
for instance, remove an explicit null-check if the parameter is declared as nonnull
— even in code outside of the function with
the attribute.
Note that the nonnull
attribute is a GNU extension (see nonnull and returns_nonnull) which many
compiler vendors have implemented.