7

The problem is this:

#define do_stuff(ret) ((ret) ? getstuff(ret) : 0)
int var;
do_stuff(&var);

test.h:34:46: warning: the address of 'var' will always evaluate as 'true' [-Waddress]

do_stuff acts as a function that accepts an output-pointer that can be NULL, though, thus the warning is not helpful but annoying. Is there a way via code to make gcc stop complaining? Maybe an (at least kind of) portable one?

Btw. do_stuff has to be a macro as ret actually gets set in a generic way (here stripped for simplicity).

Edit: Again, I just want to have the usual output-pointer that can be NULL, but inside a macro instead of a function. The actual code looks like this:

#define retrieve(x, ret) \
    ( ret ? (*ret = x.buf[0], 1) : 0 )

which gave the warning from above when using it like retrieve(stuff, NULL). Accordingly to Adriano Repetti's answer, I changed it to:

#define retrieve(x, ret) \
    ( ((void *)ret != NULL) ? (*ret = x.buf[0], 1) : 0 )

which works, but now gives me warning: dereferencing 'void *' pointer as this gets expanded to ( ((void *)NULL != NULL) ? (*NULL = x.buf[0], 1) : 0 ). Is there a way I can get rid of this warning, too?

retrieve has to be a macro because x.buf is of variant type, and so is ret, passing it through a function like in 2501's tip would result in type loss.

netcat
  • 396
  • 1
  • 5
  • 14
  • What do you mean "`ret` actually gets set in a generic way"? Is it always an `int*`? If not can you use a `void*` in the function prototype? – Degustaf Nov 20 '14 at 20:19
  • Like in "only macros are capable of doing that"-generic, if I used a `void *`, I would need to `memcpy`, which slows down the code. – netcat Nov 20 '14 at 20:32
  • the contents of var might possibly be 0/NULL however the address of a variable, especially on the stack, will always be some non-zero value. so the compiler is correct. perhaps what you really want is to pass the contents (pass by value) of the var variable. if so, then remove the '&' from the invocation of the macro – user3629249 Nov 20 '14 at 22:41
  • 1
    @netcat about your last edit: you may introduce a dummy local variable: *((void*)ret != NULL) ? &dummy : ret) = x.buf[0], 1 – Adriano Repetti Nov 21 '14 at 10:15
  • Nice idea. `((*(((void *)(ret) != NULL) ? (ret) : &dummy)) = x.buf[0], 1)` works with _NULL_ and _&var_. However, the downside of this is 1. that with _ret = NULL_ there is a dummy assignment emitted and 2. in this particular example you couldn't return 0 when there is no pointer supplied. I was hoping the compiler wouldn't validate the `*NULL = x.buf[0]` since `(NULL != NULL) ?` will never be the case.. – netcat Nov 21 '14 at 14:05
  • @Adriano Repetti: Oops, sorry. I could've just combined both of your answers to `((void *)(ret) != NULL) ? ((*(((void *)(ret) != NULL) ? (ret) : &dummy)) = x.buf[0], 1) : 0`. It's kind of ugly and redundant, but it works. Again, smart thinking of yours. – netcat Nov 24 '14 at 01:17
  • 1
    @netcat yes it looks pretty weird also to me, good candidate for a long comment or hmmmm some nested macros or hmmmm I don't know...here I miss C++ templates – Adriano Repetti Nov 24 '14 at 08:23

3 Answers3

10

Assuming you really can't avoid a macro and considering that I wouldn't disable warnings (even if this particular one isn't dangerous) then I'd cheat compiler with some casts. Code will be portable and no warnings will be emitted:

#define do_stuff(ret) (((uintptr_t)NULL != (uintptr_t)ret) ? getstuff(ret) : 0)

Central point here is simply this: (uintptr_t)NULL != (uintptr_t)ret. I'd suggest to also read this post here on SO. Note that also simply NULL != (void*)ret works.

Community
  • 1
  • 1
Adriano Repetti
  • 65,416
  • 20
  • 137
  • 208
  • @2501 I don't know...I hope he/she will explain! – Adriano Repetti Nov 20 '14 at 20:13
  • What compiler are you using? This appears to be compiler dependant, mine retains the warning for `!= NULL`, but no warning for `(uintptr_t)NULL !=`, and no warning for my approach. – 2501 Nov 20 '14 at 20:26
  • Exactly what I wanted. I kind of thought that casting might hinder the compiler from emitting warnings. I didn't test it though. Thanks – netcat Nov 20 '14 at 20:40
  • 2
    @2501 gcc 4.8.1, I like your approach too: it introduces a fake function but it'll be optimized away (in general and especially with macros, casting may hide terrible bugs) – Adriano Repetti Nov 20 '14 at 20:53
  • I can now answer my own comment, as to why this is downvoted. Unlike pointer comparison, comparisons using pointers which are cast to uintptr_t, is not defined in C. – 2501 Aug 06 '16 at 15:27
  • @2501 why? You can always cast to/from void* and it's a normal integer type (where comparison is safest thing you can imagine to do). Why it's UB? – Adriano Repetti Aug 06 '16 at 17:49
  • @AdrianoRepetti Because C Standard doesn't define it. Two pointers to the same object may have different bit representation. – 2501 Aug 07 '16 at 05:49
  • @2501 I admit I can't understand what you mean. C standards define conversions to/from uintptr_t. C standards define operations between uintptr_t. That's all we need to know, we're not _manipulating_ uintptr_t before converting back to T*. In this case we don't even need to worry about normalization. In short: can we convert any pointer to uintptr_t and compare it with something? Yes, perfectly allowed (and defined because we're using uintptr_t not an arbitrary integer type). – Adriano Repetti Aug 10 '16 at 21:35
  • 2
    Two pointers to the same object may have different bit representation. The comparison `==` of those two pointers will nevertheless yield true. Alas when those two pointers are converted to uintptr_t, comparisons `==` of those two integers may not yield true. This didn't happen on the architecture you're using, but C allows such an architecture, which means that this code will not work correctly on such an architecture. – 2501 Aug 11 '16 at 06:04
  • I understand that, however here we do not rely on what you're mentioning. We convert to uintptr_t and compare with 0 (which is a well-defined value in C). There isn't any other assumption. – Adriano Repetti Aug 11 '16 at 06:59
3

You can add a dummy function that just returns the pointer, this might silence the compiler:

void* pass( void* a )
{
    return a ;
}

#define do_stuff(ret) ( pass(ret) ? getstuff(ret) : 0)
int var;
do_stuff( &var );
2501
  • 25,460
  • 4
  • 47
  • 87
2

GCC's -Waddress command line flag is used to disable this type of warnings. But it's better to write your code without warnings.

Eugene Sh.
  • 17,802
  • 8
  • 40
  • 61