rajesh6115
rajesh6115

Reputation: 735

how to generate warning of non-null argument for my custom function?

I am trying to write a library function where arguments should not be null, and want that gcc should generate warning if some one try to pass NULL. My code is

#include <stdio.h>
#include <string.h>
int own_strcmp(const char *str1, const char *str2)
{
        if( str1 == NULL || str2 == NULL){
            if(str1 == NULL && str2 == NULL) 
                return 0;
            else if( str1 == NULL)
                return str2[0];
            else
                return str1[0];
        }
        int i=0;
        while(str1[i] && str2[i]){
                if( str1[i] != str2[i]){
                        break;
                }
                i++;
        }
        return str1[i]-str2[i];
}



int main(int argc, char *argv[]){
        const char *str1 = "hello";
        const char *str2 = "hello";
        printf("%s and %s is %d\n", str1, str2, own_strcmp(NULL, str2));
        printf("%s and %s is %d\n", str1, str2, strcmp(NULL, str2));
        return 0;
}

for strcmp of standard library it use to generate warning. but for my function it is not.

rajesh@ideapad:~$ gcc own_strcmp.c
own_strcmp.c: In function ‘main’:
own_strcmp.c:21:2: warning: null argument where non-null required (argument 1) [-Wnonnull]
  printf("%s and %s is %d\n", str1, str2, strcmp(NULL, str2));
  ^

Upvotes: 2

Views: 4469

Answers (3)

rich
rich

Reputation: 61

It's pretty easy to put in a check like this in each function that calls private functions to force the callee to check their params.

// example follows. note this isn't production code.
// this forces

// declare a function which should never be called.
void _abort_build() __attribute__ ((error("programming error detected")));


// This function is a barrier, obliging all calling code to ensure it never passes nullptr
// it's in effect a contract, stating what is expected.
static void private_api(const char * p)
{

    if(!p)
    {
        _abort_build();
    }

    __builtin_printf("%s\n", p);

}

// note this is just an example
void public_api(const char * p)
{
    // EXAMPLE, this pointer check is needed, the code calling private_api() must check first
    //if(p)
        private_api(p);

    if(!p)
        __builtin_printf("public_api error: called with nullptr\n");
}



int main()
{
    public_api(nullptr);
}

https://godbolt.org/z/43Tjxxz85

Upvotes: 0

Eric Postpischil
Eric Postpischil

Reputation: 223663

C has a method for declaring a pointer parameter that must not be null. However, it cannot guarantee that a null pointer is never passed in error. It advises the compiler that it can compile the routine in the expectation that the parameter will never be null, but it does not necessarily prevent calling routines from incorrectly passing null. A compiler may detect some incorrect calls to such a routine and issue warnings or errors about them but fail to detect others.

This is done by the rarely used syntax of putting the static keyword inside the parameter declaration:

void foo(int p[static 1])
{
    … body of function …
}

This says that p must provide access to the first element of an array of at least one element, per C 2011 [N1570] 6.7.6.3 7. Since there must be an element where p points, p may not be null. For example, when this code is compiled:

#include <stddef.h>

void foo(int p[static 1])

void bar(void) { foo(NULL); }

with Apple LLVM 9.0.0 (clang-900.0.39.2) with default switches, the compiler warns:

x.c:5:18: warning: null passed to a callee that requires a non-null argument
      [-Wnonnull]
void bar(void) { foo(NULL); }
                 ^   ~~~~
x.c:3:14: note: callee declares array parameter as static here
void foo(int p[static 1]) {}
             ^~~~~~~~~~~

However, this code compiles with no warning:

#include <stddef.h>

void foo(int p[static 1]) {}

void bar(int *p) { foo(p); }

void baz(void) { bar(NULL); }

So it seems this compiler is able to detect that null is incorrectly passed for such a parameter only when it is directly passed.

Upvotes: 5

ad absurdum
ad absurdum

Reputation: 21365

Perhaps declaring the GCC nonnull function attribute is what you are looking for. You can specify that all pointer arguments should be non-null, or you can provide an argument list specifying which arguments should be non-null.

The way I read the docs, if the nonnull attribute is declared, it is undefined behavior to pass a null pointer to a non-null parameter. The -Wnonnull option must be enabled to get the warning. With warnings enabled, if NULL is passed to a non-null parameter, a warning is issued. But, no warning will be issued for a null pointer variable that is passed. Thus it is important to handle incorrect input at runtime. Thanks to @alk for pointing out this important issue.

It looks like Clang also supports this attribute. Clang also has the nullability type qualifier _Nonnull, but this is not available in GCC.

I should emphasize that this behavior is not supported in the C Standard, but is rather a compiler extension. For portability you should handle incorrect inputs at runtime with well-designed functions.

Here is your function (the earlier version without NULL pointer checks) declared with the attribute. I have added a line that calls own_strcmp() with a null pointer variable as well; note that this line does not cause a warning to be issued. Also note that calling the Standard Library strcmp() with a null pointer variable fails to trigger a warning.

#include <stdio.h>
#include <string.h>

int own_strcmp(const char *str1, const char *str2)__attribute__((nonnull));

int main(void)
{
    const char *str1 = "hello";
    const char *str2 = "hello";
    const char *arg = NULL;

    /* Warning issued for this NULL argument */
    printf("%s and %s is %d\n", str1, str2, own_strcmp(NULL, str2));

    /* No warning issued for the NULL variable arg */
    printf("%s and %s is %d\n", str1, str2, own_strcmp(arg, str2));

    /* No warning issued for strcmp() either */
    printf("%s and %s is %d\n", str1, str2, strcmp(arg, str2));    
    return 0;
}

int own_strcmp(const char *str1, const char *str2)
{
    int i=0;
    while(str1[i] && str2[i]){
        if( str1[i] != str2[i]){
            break;
        }
        i++;
    }
    return str1[i]-str2[i];
}

Here is the output after invoking GCC:

λ> gcc -std=c11 -Wall -Wextra -Wpedantic warning_gcc_42035769.c
warning_gcc_42035769.c: In function ‘main’:
warning_gcc_42035769.c:13:5: warning: null argument where non-null required (argument 1) [-Wnonnull]
     printf("%s and %s is %d\n", str1, str2, own_strcmp(NULL, str2));
     ^

Upvotes: 6

Related Questions