3

I have a struct where I use bitfields to optimize memory. I have a uint64_t type and I want to print its value. When compiled it shows me this warning: format ‘%lu’ expects argument of type ‘long unsigned int’, but argument 5 has type ‘long unsigned int:48’ [-Wformat=]

I have already tried to supress this warning by typing while compiling -Wno-format. I'm wondering if there are a better way to do it.

Here some code:

#include <stdint.h>
#include <stdio.h>

typedef struct gn_addr 
{
    unsigned int m:1;
    unsigned int st:5;
    unsigned int reserved:10;
    uint64_t mid:48;
} gn_addr ;

void gn_addr__print(gn_addr *self)                                                                                                                                                                         
{                                                                                                                                                                                                          
    printf("M=>%d\nST=>%d\nReserved=>%d\nMID=>%lu\nTotal size %ld bytes\n",                                                                                                                                
         self->m, self->st, self->reserved, self->mid, sizeof(self));                                                                                                                                   
} 
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
Joshua
  • 57
  • 4
  • Since you are wildly mixing types and using non-standard bit-field types, the compiler is free to store `m`, `st` and `reserved` in one 32 bit variable and then `mid` in a separate `uint64_t` variable. So your "optimization" can lead to significantly increased memory use, in addition to turning the code completely non-portable. It would have been wiser to drop bit-fields and simply use a single `uint64_t` variable instead. – Lundin Apr 03 '19 at 09:35
  • 1
    Note that `sizeof(self)` is the size of the **pointer** `gn_addr *self` and not the size of the structure. – Andrew Henle Apr 03 '19 at 09:41

3 Answers3

2

First, use proper format specifier for uint64_t and size_t types, use PRIu64 macro, as defined in inttypes.h and %zu.

That said, for the bit field variables,

  • you can either use a cast, like (uint64_t)self->mid
  • or you can use an intermediate variable of type uint64_t, assign the member variable value to the new intermediate variable, and pass that to printf() as the corresponding argument to the format specifier, like

    uint64_t temp = self->mid;
    printf("%"PRIu64 "\n", temp);
    
Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
  • 1
    I'm still getting the same warning. I've also did `printf("%s\n", PRIu64)` and it outputs `lu` – Joshua Apr 03 '19 at 07:33
2

While you should definitely apply the fixes in the other answers to get portable format specifiers, the warning will persist. The reason is that extra arguments to a variadic function like printf undergo argument promotions. Argument promotions include integer promotion.

The rules for integer promotions will convert any integer with a conversion rank less than int/unsigned, as well as bit-fields, into the an int/unsigned. So for your initial bit-fields, you get int automatically.

For integers with a higher conversion rank than int/unsigned, no promotion occurs. So your bit-field is not promoted to an uint64_t, and you get a warning about argument mismatch. You need a cast.

(uint64_t)self->mid

Btw, since nobody mentioned, the portable format specifier for a size_t (the type of the sizeof operator) is %zu. You should use that instead of %ld.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • It works perfectly. Thank you by sharing your knowledge! – Joshua Apr 03 '19 at 07:41
  • In fact C doesn't have built-in standard support for `uint64_t` bit-fields, so what will happen is up to the compiler entirely. – Lundin Apr 03 '19 at 09:39
0

Functions like printf are being examined by compilers (not all). The compiler checks if format specifier matches with the type of argument passed. You need to make explicit conversion to make that Warning go away. As mentioned in other answers that there are some implicit integer promotion rules and you need to be aware of them (Thanks to @storyTeller).

The rules for integer promotions will convert any integer with a conversion rank less than int/unsigned,as well as bit-fields, into the an int/unsigned. So for your initial bit-fields, you get unsigned int automatically.

For integers with a higher conversion rank than int/unsigned, no promotion occurs. So your bit-field is not promoted to an uint64_t, and you get a warning about argument mismatch. You need a cast.

Try this:

(uint64_t)self->mid

I have already tried to supress this warning by typing while compiling -Wno-format. I'm wondering if there are a better way to do it.

It's not a good idea to suppress your warnings. They are generated for a reason. I would recommend to use warning flags like -Wall, -Wshadow, -Wextra and other important flags. They can help you with deploying code with lesser number of bugs.

Regarding your assumption:

I have a struct where I use bitfields to optimize memory.

I would like to point out that your struct with bitfields won't be that much memory optimized as you might be considering it. There is something called Padding and Data Alignment which may help you further in reducing the memory footprint.

Check this question.

abhiarora
  • 9,743
  • 5
  • 32
  • 57