指针类型 intptr_t 和 uintptr_t 的字符串格式化方式

42

intptr_tuintptr_t在32位和64位体系结构中的字符串格式是什么?

编辑:

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

我在64位系统上收到了这个警告,但在32位系统上没有。

  intptr_t  AAA
6个回答

74

这些宏定义位于inttypes.h头文件中:

对于printfPRIdPTR PRIiPTR PRIoPTR PRIuPTR PRIxPTR PRIXPTR

对于scanfSCNdPTR SCNiPTR SCNoPTR SCNuPTR SCNxPTR

使用示例:

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

24
使用示例:uinptr_t p = SOME_VALUE; printf("这是一个指针:% " PRIxPTR "\n", p); - Fred Foo
感谢您的回答,我的问题可能没有那么精确...我想知道是否有像 "%d" 针对整数等类型有强格式化的方式。我根据硬件架构得到了一些警告。我已经编辑了我的问题。 - thetna

2

我认为你应该考虑使用 'z' 修饰符。它可以将与 size_t 或 ssize_t 相对应的任何内容转换,并且我发现它也适用于 (u)intptr_t。

例如:

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


1
恭喜你,你发现了未定义的行为。 - Antti Haapala -- Слава Україні
@AnttiHaapala 如果 void *p = ...; printf("%td", (ptrdiff_t)p); 会产生(同样类型的)未定义行为吗? - dxiv

0
####################################### 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;
}

抱歉,但这个问题被标记为C语言,为什么要用C++的解决方案来回答呢? - Stargateur
使用“#include <typeinfo>”需要cpp后缀。据我所知,printf是C函数,而不是CPP函数,std::printf是CPP函数。 - ZHOU Ling
@Stargateur 你说这是一个cpp解决方案,只是因为有cpp后缀吗? - ZHOU Ling
1
typeid 在 C 中不存在,所以...... - Stargateur
为 C 添加了一个通用版本。 - ZHOU Ling
test.{cpp|c} 仅用于证明 intptr_t 与 ptrdiff_t 相同,ptrdiff_t 具有标准字符串格式 %t(man 3 printf)。因此,intptr_t 具有字符串格式 %td,uintptr_t 具有字符串格式 %tu。我认为我已经回答了这个问题。无论如何,我不想再在这个问题上浪费时间了。 - ZHOU Ling

0

%p 应该可以替换 %x,因为在该平台上,uintptr_t 被定义为与指针大小相同的无符号整数。

编辑:不幸的是,在我的编译器上,您必须将变量转换为(void *)。但是,我认为将 uintptr_t 转换为指针是安全的。


-1

我正在一个环境中编译一些代码,但由于某种原因,inttypes.h 中的 PRI.PTR 宏未定义,并且在其中 intptr_t 在 32 位上被定义为 int,在 64 位上被定义为 long int

我通过在 printf 参数中使用 %li 格式说明符并将变量强制转换为 long int 来解决了警告。在这个环境中,这是安全的,因为如上所述,intptr_t 永远不会比 long int 更长。

如果可以避免,我不建议使用这种解决方案,但至少它解决了警告。


如果你必须使用强制转换,那么请使用%jd(%ju、%jx、%jo),并将其转换为intmax_t(/uintmax_t)。 - William Cushing
@WilliamCushing 那是一个更好的主意。谢谢! - Rich

-1

我认为即使是long int也不安全,你应该尝试使用long long int,因为你正在使用64位架构并且已经有了intptr_t

在一些64位架构上(我认为微软Windows就是这样),long int可能会保持32位宽度,以满足MS-DOS时代的假设,即short int始终为16位,而long int始终为32位。

即使在那些具有32位long int的平台上,printf("%llx", (unsigned long long)AAA);也可以工作。如果可能的话,你应该考虑更可取的形式printf("%jx", (uintmax_t)AAA);

请注意,对于一些旧编译器,你可能需要使用"%Lx"(对于GNU C,需要将其转换为unsigned long long)或"%I64x"(对于Visual C++,需要将其转换为__uint64)来表示64位整数。

附言: 在这种情况下,%p 可能不是很好,因为 %p 可能会在十六进制数之前打印裸字 0x,并/或者可能会打印零填充值。如果两者都应用了,例如,代码 printf("%p\n", (void*)16); 将在 32 位平台上打印 0x00000010,在 64 位平台上打印 0x0000000000000010;发帖人应该希望只打印 10


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接