Sentinel warning: missing sentinel in function call
GCC 4 warnings about sentinels
Null-terminate parameter list
Some functions have a variable number of arguments, where the argument list has to be terminated with a NULL value. To solve this warning, null-terminate the list of parameters.
Introduction
When compiling using GCC 4, you may get one of the following warnings:
warning: missing sentinel in function call
warning: not enough variable arguments to fit a sentinel
This document explains what these warnings mean and how to resolve them.
Sentinels & warnings
A sentinel is a special value which indicates the end of a series of data. In the context of GCC, it is a null pointer which ends a list of parameters to a function.
The function
execl(const char * path, const char * arg, ...)
takes a variable amount of parameters. To know where to stop reading parameters, the last parameter must be a null pointer. When you do not end the list with a null pointer, execl will not stop scanning, which will result in errors. The following code, therefore, is wrong:
#include <unistd.h> int main() { execl("/bin/ls", "ls", "-l"); return 0; }
The execl function call is not terminated by a null pointer and GCC will give a warning. The following code is better:
#include <unistd.h> int main() { execl("/bin/ls", "ls", "-l", NULL); return 0; }
As you can see, the execl function is now terminated by a null pointer
and it will stop scanning it's arguments after that. However, GCC 4 will
still issue a warning:
warning: missing sentinel in function call
This is because NULL is not of the right type: it is defined as integer 0 instead of a pointer with the value 0.
Doing an explicit cast can make the warning
go away:
#include <unistd.h> int main() { execl("/bin/ls", "ls", "-l", (char *)NULL); return 0; }
Using sentinels
One can specify that a function uses a sentinel by declaring it as follows:
char * build_path(const char * str, ...) __attribute__((__sentinel__(0)));
This indicates that the parameter list is ended with the special value 0, which must be a char pointer.
On most systems, there is no difference between 0 and (char *)0. On 64 bit systems, however, the integer 0 is 32 bits and the pointer 0 is 64 bits. The compiler does not know whether it is an integer or a pointer, and defaults for the integer. This will not clear the upper 32 bits and the function will not stop scanning its parameters.