2

I am trying to use the stdout stream to print 0x00000000 instead of (nil) and 0x08ffffff instead of 0x8ffffff.

So basically, char *ptr1 = 0x00000000 and char *ptr2 = 0x08ffffff.

When I run

printf("start of address is %p and end of address is %p", ptr1, ptr2);

I get

start of address is (nil) and end of address is 0x8ffffff

I actually found a way around this and I would actually do this:

char *ptr1 = 0x00000000; 
char *ptr2 = 0x08ffffff;
printf("start 0x%08x and end 0x%08x\n",ptr1, ptr2);

This generates

start 0x00000000 and end 0x08ffffff

However, the compiler sends the following warning:

warning: format ‘%x’ expects argument of type ‘unsigned int’, 
but argument 3 has type ‘void *’ [-Wformat=]

How should I modify printf to adjust the output print?

Hadi Rohani
  • 215
  • 4
  • 14

1 Answers1

3

First, to have any control over how addresses are printed, you must convert them to an integer type. The specifications for printing pointers with %p are inadequate. To do this, you can include <stdint.h> and use the type uintptr_t, which is an unsigned integer type suitable for some work with addresses.

Second, the header <inttypes.h> provides a macro PRIxPTR that provides a printf conversion specifier for printing uintptr_t values in hexadecimal.

Third, you can add a flag 0 and a minimum field width 8 to request the conversion pad with zeros to at least eight digits.

Here is sample code:

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

static void PrintAddress(const void *p)
{
    uintptr_t u = (uintptr_t) p;
    printf("0x%08" PRIxPTR, u);
}

int main(void)
{
    char *ptr1 = (char *) 0x00000000;
    char *ptr2 = (char *) 0x08ffffff;
    printf("Start of address is ");
    PrintAddress(ptr1);
    printf(" and end of address is ");
    PrintAddress(ptr2);
    printf(".\n");
}

Better yet, you can adapt the field width to the size of uintptr_t in your C implementation by including <limits.h> and calculating the number of digits needed:

    printf("0x%0*" PRIxPTR, ((int) sizeof u * CHAR_BIT + 3) / 4, u);

Note that directly setting pointers to hard-coded constant values, as you do with the initializations of ptr1 and ptr2, is rare. It is generally used in doing special things with hardware. When you are deliberately converting an integer to a pointer, you should use a cast to avoid a compiler warning.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 1
    how about `(sizeof u * CHAR_BIT + 3) / 4` so that it works on my 9-bit char / 18-bit address computer :-P – Antti Haapala -- Слава Україні Feb 02 '21 at 17:43
  • @AnttiHaapala: Okay. – Eric Postpischil Feb 02 '21 at 17:45
  • note that it's possible a conforming system won't support `uintptr_t` . In which case we can loop through the bytes of the number (obtained via whichever method desired) printing two nibbles at a time – M.M Feb 02 '21 at 19:52
  • @M.M: Well, I would write code for that, but to support it and the non-8 `CHAR_BIT` implementation, we would have to write code that handles hexadecimal digits split across the implementation’s “bytes,” i.e., maintain a bit-shift buffer. Or otherwise accommodate the odd-size bytes. I think I will not go that far right now. – Eric Postpischil Feb 02 '21 at 19:59