Apr 16, 2025
GCC and Clang provide the __attribute__((nonnull))
or gnu::nonnull
attribute, a compile-time mechanism to enhance code safety by allowing you to declare that specific function pointer arguments must never be NULL
.
This has the following benefits:
NULL
pointer errors during compilation, preventing runtime crashes.NULL
, the compiler can perform more aggressive optimizations, potentially leading to faster code.nonnull
attribute acts as a clear contract, communicating to developers which arguments are expected to be valid pointers.You can apply the nonnull
attribute in two ways:
__attribute__((nonnull))
: This form indicates that all pointer arguments of the function must be non-NULL
.__attribute__((nonnull(arg_index_1, arg_index_2, ...)))
: This allows you to specify which specific pointer arguments (using 1-based indexing) are expected to be non-NULL
.C++ users might encounter the [[gnu::nonnull(index)]]
syntax, which is a GNU extension even within C++11 attribute brackets.
Be aware that some compilers might warn or ignore this form.
void process_data(int *data, size_t size) __attribute__((nonnull(1)));
void test_process() {
process_data(NULL, 10); // Compiler may warn: argument 1 is NULL
int values[] = {1, 2, 3};
process_data(values, sizeof(values) / sizeof(int)); // OK
}
[[gnu::nonnull(2, 4)]]
int calculate_result(const char *operation, const int *values,
size_t count, int (*process)(int));
void test_calculation() {
int numbers[] = {5, 10};
calculate_result("+", numbers, 2, NULL);
// Compiler may warn: argument 4 is NULL
}
First, for this warnings to be emitted, -Wall
must be passed to the compiler.
test.c: In function 'test_process':
test.c:4:5: warning: argument 1 null where non-null expected [-Wnonnull]
4 | process_data(NULL, 10); // Compiler may warn: argument 1 is NULL
| ^~~~~~~~~~~~
test.c:1:6: note: in a call to function 'process_data' declared 'nonnull'
1 | void process_data(int *data, size_t size) __attribute__((nonnull(1)));
| ^~~~~~~~~~~~
test.c: In function 'test_calculation':
test.c:15:5: warning: argument 4 null where non-null expected [-Wnonnull]
15 | calculate_result("+", numbers, 2, NULL);
| ^~~~~~~~~~~~~~~~
test.c:10:5: note: in a call to function 'calculate_result' declared 'nonnull'
10 | int calculate_result(const char *operation, const int *values,
| ^~~~~~~~~~~~~~~~
Second, remember to apply the nonnull
attribute during the function declaration, not within the function definition itself.
// Correct
void analyze_string(const char *str) __attribute__((nonnull(1)));
void analyze_string(const char *str) {
// ... implementation ...
}
// ~~~
// Incorrect:
void analyze_string(const char *str) __attribute__((nonnull(1))) {
// ... implementation ...
}
While nonnull
is a fantastic tool for compile-time safety, it's crucial to understand its limitations.
The compiler can only warn about NULL
values that it can determine statically (e.g., literal NULL
).
In dynamic scenarios where a pointer might become NULL
during runtime, the nonnull
attribute won't provide protection.
Therefore, especially for public APIs or library functions, it's still best practice to include runtime checks (like assert(ptr != NULL)
for debugging or explicit if (!ptr) return;
for production) to handle potential NULL
values gracefully.
The __attribute__((nonnull))
attribute in GCC and Clang is a valuable asset in writing safer and more efficient C and C++ code.
By clearly specifying which pointer arguments should never be NULL
, you empower the compiler to help you catch potential errors early, leading to more robust and maintainable software.