-3

I have warnings when I try to compile the code. I know it has something to do with the 64-bit Architecture. But I am not able to fix it.

#define GETFUNC(x,y)    x = (void *)dlsym(hLib, y); \
                        if(!quiet) printf("  %-10s %08x\n", y, (uint32_t)x);

warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]

if(!quiet) printf("  %-10s %08x\n", y, (uint32_t)x)
Haris
  • 12,120
  • 6
  • 43
  • 70
  • use `%p` to print the pointer. – kiran Biradar Jun 21 '19 at 09:00
  • What is the purpose of casting your pointer `x` to `(uint32_t)`?? – Haris Jun 21 '19 at 09:01
  • 1
    Compilers often warn about converting pointers to integral types of different sizes, because the conversion may lose information (e.g. change the value in a way that can't be recovered). The fact you are trying to use type conversions to make your code work is a danger sign. Incidentally, printing a `uint32_t` using the `%x` format also gives undefined behaviour. Solution: don't convert `x` to be `uint32_t` and print it using the `%p` format. – Peter Jun 21 '19 at 09:03
  • I printed it using %p. Thanks alot guys. The code works now. – Prinoy D'Costa Jun 21 '19 at 09:14
  • 1
    @Aconcagua - I didn't down-vote, but I suspect one reasons others did is that the OP could have easily found an answer with a little effort to read documentation for their compiler. In other words, a down-vote is often used when the inclination is to simply reply "RTFM". – Peter Jun 21 '19 at 10:49

2 Answers2

4

Well, on your system, pointers obviously are 64 bit. These do not fit into the 32-bit value you cast to, thus you get a warning.

To cast pointers to integers, you should go via uintptr_t:

printf("  %-10s %08x\n", y, (uint32_t)(uintptr_t)x);
//                 ^ however, what, if uint32_t is long on your system???
//                   coming to later...

First cast makes an integer of appropriate size from the pointer, second cast tells the compiler that you explicitly want to truncate the value – and it won't issue a warning any more as it assumes it is what you want to do.

But: You are cutting off the upper half of the address information! If you use this, you might end up in seeing two totally different variables at the same memory location, because the addresses might have differed only in upper half.

You might live with for having shorter logs ('collision' probability shouldn't be too high), the correct way, though, would be outputting via appropriate pointer format specifier %p:

printf("  %-10s %p\n", y, x);        // usually no cast needed, but actually incorrect
printf("  %-10s %p\n", y, (void*)x); // fully compliant variant

This will print all eight bytes (16 nibbles) correctly on your system, four bytes (8 nibbles) on a 32-bit system. If you want to have all eight bytes on any system (for uniform logs), then you can go via appropriate casts again:

printf("  %-10s %016llx\n", y, (uint64_t)x);
//                   ^ uint64_t most likely is unsigned long long
//                     but on 64-bit linux, just unsigned long!!!

Casting to uintptr_t is not necessary this time, as any pointer would fit into – and some time in the future, when the first 128-bit systems appear, you might even want the warning to re-appear (and if really not, add the intermediate cast again). Edit: As denoted, not fixing the warning would result in undefined behaviour, though. So to be on the safe side, you'd rather explicitly catch the issue via static_assert(sizeof(uintptr_t) <= sizeof(uint64_t)), which would enforce adjustment (thanks, chux, for the hint).

Well, correct format specifier... Luckily, there are appropriate macros defined, although usage is not really nice:

printf("  %-10s %016" PRIx64 "\n", y, (uint64_t)(uintptr_t)x);

(would have been PRIx32 in initial example).

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • 2
    Re: future 128 pointer, the cast, omitting the `(uintptr_t)` step risks UB "If the result cannot be represented in the integer type, the behavior is undefined.". A one step cast of `(uintmax_t)` with `"%jX"` would be more resilient. – chux - Reinstate Monica Jun 21 '19 at 12:34
  • @chux Hm... that again wouldn't make output look all alike on all systems (32, 64, 128 bit, which is how I got to this point at all) – possibly better just to catch via `static_assert(sizeof(uintptr_t) <= sizeof(uint64_t)`, as we'd probably *want* to modify the code then... – Aconcagua Jun 22 '19 at 11:26
  • Printing `void*` pointers with `"%p"` is the most informative. Any conversion to an integer can lose information. – chux - Reinstate Monica Jun 22 '19 at 12:10
  • @chux Absolutely agree on the format specifier. Did I make it sufficiently clear that this is the version I recommend? Rest is only if I want to guarantee a specific number of nibbles in the output (cannot do so with `%p`). *'any conversion can lose'* – you surely mean, if not assuring the integer having appropriate size by whatever means? – Aconcagua Jun 22 '19 at 15:20
  • A printed _pointer_ need not be wholly numeric. A printed value could be "stack:1234" or "0020:0234" versus an integer with "0xA6234". With common flat memory models, little may be lost converting to an integer first, yet C supports other models. – chux - Reinstate Monica Jun 22 '19 at 18:20
  • @chux But that's just a matter of representation. According to 7.20.1.4: *'[...] any valid pointer to void can be converted to this type, then converted back to pointer to void, and the result will compare equal to the original pointer[...]'*. So whatever the pointer in question actually represents (and to whatever interesting format via `%p` we might get), it must be reconstructible by some means from the integral value, be it by a simple equality of value or by some complex algorithm... – Aconcagua Jun 22 '19 at 23:24
  • True about _compare equal_, yet information can be lost as there can be multiple pointers that convert to a integer and then back to a pointer. The final pointer _compares equal_ to the originals, yet need not need be one of the original bit patterns - hence information loss. – chux - Reinstate Monica Jun 23 '19 at 00:28
1

uint32_t = 32 bit.

Your pointer = 64 bit.

Hence the warning. You can use %p to print the pointer, or, if you want an integer, cast to size_t and use the z modifier (SO here).

Michael Chourdakis
  • 10,345
  • 3
  • 42
  • 78