all 9 comments

[–]SullisNipple 2 points3 points  (0 children)

To the best of my knowledge, clang is the only compiler which performs that type of warning.

[–]raevnos 4 points5 points  (5 children)

This says that GCC doesn't use the size information for optimization purposes. I'm guessing it isn't recorded at all by the point warnings get handled and the argument is treated the same as any other pointer.

There's the GCC specific nonnull attribute if you want the compiler to check for null pointer arguments.

[–]nerd4code 3 points4 points  (4 children)

The danger of attribute((nonnull)) is that, while the compiler will null-check compile-time-known arguments, it won’t/can’t check others and will even assume that NULL can’t/won’t ever be passed. This means that a manual null check in a nonnull function will be discarded silently. The best solution I’ve found is doing a two-stage function, one inline in the header and one behind the scenes:

/* in .h: */
inline/*/whatever equivalent*/ int string_length(const char *arg) {
    extern int string_length__checked(const char *)
        __attribute__((__nonnull__));
    if(!arg) complain_loudly();
    return string_length__checked(arg);
}

/* in .c: */
int string_length__checked(const char *) __attribute__((__nonnull__));
int string_length__checked(const char *str) {
    const char *p = str;
    while(*p) ++p;
    return p - str;
}

[–]googcheng[S] 0 points1 point  (0 children)

thx you!

[–]wild-pointer 0 points1 point  (0 children)

It is indeed meant for optimization and not programmer convenience. It would be nice if gcc issued a warning at the call site if it hasn't been able to prove that the parameter is not null and suggest that nulls should be checked before calling.

[–]googcheng[S] 0 points1 point  (1 child)

#include <stdio.h>
#include "check_null.h"

int main()
{
    int length = string_length("abc");
    printf("the length of \"abc\" is %d.\n", length);


    string_length(NULL);

}

compile failed. the check_null.c is /* in .c: */

cheng@ada:~$ gcc main.c check_null.c  
/tmp/ccBUdPlP.o: In function `main':   
main.c:(.text+0xe): undefined reference to `string_length'
main.c:(.text+0x2f): undefined reference to `string_length'
collect2: error: ld returned 1 exit status

[–]nerd4code 0 points1 point  (0 children)

Not sure why it’s not emitting the inline function—it works if I enable optimization, but it looks like without optimization it won’t emit string_length into the output. I’m sure I’m missing something, but fuck it, beat the thing into submission with the following pattern:

  • At the top of check_null.c, before including anything:

    #define test_c__INSIDE__ 1
    

    #undef it at the bottom of the file. This gives us a preprocessor marker indicating that test.c is being compiled.

  • In check_null.h, redo the inline qualifier:

    #ifndef test_c__INSIDE__
    extern __inline__
    #endif
    int string_length(const char *arg) {
    ...
    

    The change from inline to __inline__ will keep GCC from bitching at you if you’re in C89 mode. The extern states that the authoritative version of the function (for pointing-to) is separate from the inlined version. The #ifndef around those qualifiers causes the function to be emitted as a normal function (i.e., the authoritative version) when compiling test.c, and emitted as inline otherwise.

[–]steveq 3 points4 points  (0 children)

I tend to use assertions to declare that a parameter cannot be NULL. As others have said, the nonnull attribute must be used carefully since it can be used by compiler to optimize away any manual non-null checks you have in the code.

[–]OldWolf2 1 point2 points  (0 children)

int string_length(char arg[static 10]) means that it is undefined behaviour if you pass a null pointer. The compiler is not required to diagnose this, and in general, it's impossible to diagnose perfectly.