string format for intptr_t and uintptr_t

2019-01-06 20:50发布

问题:

What is the string format for intptr_t and uintptr_t which is valid for both the 32 and 64 bit architecture .

EDIT

warning: format ‘%x’ expects type ‘unsigned int’, but argument 2 has type "AAA"

This is the warning i am getting in 64 bit but not in 32 bit.

  intptr_t  AAA

回答1:

That would be the following macros from inttypes.h:

For printf: PRIdPTR PRIiPTR PRIoPTR PRIuPTR PRIxPTR PRIXPTR

For scanf: SCNdPTR SCNiPTR SCNoPTR SCNuPTR SCNxPTR

Usage example:

uinptr_t p = SOME_VALUE;
printf("Here's a pointer for you: %" PRIxPTR "\n", p);


回答2:

I think you should consider using the 'z' modifier. This will convert anything that corresponds to a size_t og ssize_t, and I have found that it works with (u)intptr_t as well.

For example:

intptr_t ip = ...; printf("ip = %zd\n", ip);



回答3:

I think even long int is unsafe, and you should try long long int, which must exist because you are working on a 64-bit architecture and you have intptr_t already.

On some 64-bit architectures (I think Microsoft Windows would be so), long int may be kept in 32-bit width, to satisfy the MS-DOS-age assumption that short int is always 16-bit and long int is always 32-bit.

Even on those platforms with 32-bit long int, printf("%llx", (unsigned long long)AAA); would work. And you should consider more preferrable form printf("%jx", (uintmax_t)AAA); if possible.

Note that for some old compilers you might need to use "%Lx" (for GNU C, with casting to unsigned long long) or "%I64x" (for Visual C++, with casting to __uint64) for 64-bit integers.

P. S. %p may not be good in this case, because %p may print the bareword 0x before the hexadecimals and/or may print a zero-padded value. If the both is applied, for example, the code printf("%p\n", (void*)16); will print 0x00000010 on 32-bit platforms and 0x0000000000000010 on 64-bit platforms; the poster should have hoped just 10 be printed.



回答4:

%p should work as a replacement for %x, since uintptr_t is defined to be an unsigned integer with the same size as a pointer on the platform.

EDIT: Unfortunately, at least on my compiler you have to cast the variable to (void *). However, I think it is safe to cast uintptr_t to a pointer.



回答5:

I'm compiling some code in an environment which for some reason does not have the PRI.PTR macros defined in inttypes.h, and in which intptr_t is defined as int in 32 bit and long int in 64 bit.

I hacked around the warnings by using a %li format specifier and casting the variables to long int in the printf parameters. This is safe within this environment because an intptr_t can never be longer than a long int, as described above.

I wouldn't recommend using this solution if you can avoid it, but it's solved the warnings, at least.



回答6:

####################################### CPP type proving code (identifying type by typeid)
$ cat typeid.cpp
#include <stdio.h>
#include <stddef.h>
#include <stdint.h>
#include <time.h>
#include <typeinfo>

#define name(t) printf("%30s : %s\n", #t, typeid(t).name())

// g++|clang++ -o ./typeid.exe typeid.cpp -m32 && ./typeid.exe
// g++|clang++ -o ./typeid.exe typeid.cpp -m64 && ./typeid.exe
int main(int argc, char* argv[]) {
    name(ptrdiff_t);
    name(intptr_t);
    name(uintptr_t);

    return 0;
}

####################################### C type proving code (identifying type by _Generic)
$ cat typeid.c 
#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <time.h>

/* matches the type name of an expression */
#define name_match(e) _Generic((e), \
                 _Bool: "_Bool", \
                  char: "char", \
           signed char: "signed char", \
         unsigned char: "unsigned char", \
                 short: "short", \
        unsigned short: "unsigned short", \
                   int: "int", \
          unsigned int: "unsigned int", \
                  long: "long", \
         unsigned long: "unsigned long", \
             long long: "long long", \
    unsigned long long: "unsigned long long", \
                 float: "float", \
                double: "double", \
           long double: "long double", \
               default: "unknown")

#define name(t, e) printf("%30s : %s\n", #t, name_match(e))

int main() {
    ptrdiff_t ptrdiff_v = 0;
    intptr_t  intptr_v = 0;
    uintptr_t uintptr_v = 0;

    name(ptrdiff_t, ptrdiff_v);
    name(intptr_t,  intptr_v);
    name(uintptr_t, uintptr_v);
}

####################################### run in arch32
$ clang++ -o ./typeid.exe typeid.cpp -m32 && ./typeid.exe
                     ptrdiff_t : i
                      intptr_t : i
                     uintptr_t : j

$ clang -o ./typeid.exe typeid.c -m32 && ./typeid.exe      
                     ptrdiff_t : int
                      intptr_t : int
                     uintptr_t : unsigned int
result:
     intptr_t == ptrdiff_t
    uintptr_t == unsigned ptrdiff_t

####################################### run in arch64
$ clang++ -o ./typeid.exe typeid.cpp -m64 && ./typeid.exe
                     ptrdiff_t : l
                      intptr_t : l
                     uintptr_t : m

$ clang -o ./typeid.exe typeid.c -m64 && ./typeid.exe   
                     ptrdiff_t : long
                      intptr_t : long
                     uintptr_t : unsigned long
result:
     intptr_t == ptrdiff_t
    uintptr_t == unsigned ptrdiff_t

####################################### man 3 printf
t -- A following integer conversion corresponds to a ptrdiff_t argument.

####################################### conclusion
//  intptr_t == ptrdiff_t
// uintptr_t == unsigned ptrdiff_t
// so:
//     1)  intptr_t has string format %td
//     2) uintptr_t has string format %tu

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

int main(int argc, char *argv[]) {
    intptr_t  x = 0;
    uintptr_t y = 0;

    scanf("%td %tu", &x, &y);
    printf("out: %td %tu\n", x, y);
    return 0;
}