我应该使用哪种格式指定符来打印变量的地址?我在以下三个中感到困惑。
%u - 无符号整数
%x - 十六进制值
%p - 无类型指针
哪种格式是最优打印地址的方式?
我应该使用哪种格式指定符来打印变量的地址?我在以下三个中感到困惑。
%u - 无符号整数
%x - 十六进制值
%p - 无类型指针
哪种格式是最优打印地址的方式?
(void *)
转换指针方面存在一些争议。明确表达通常是好的(所以我这样做),标准说“参数必须是指向void
的指针”。在大多数机器上,省略显式转换是可以的。但是,在位表示不同于相同内存位置的“其他任何指针”地址的给定内存位置的char *
地址的机器上会有影响。这将是一个字地址机器,而不是字节地址机器。这样的机器现在并不常见(可能也不可用),但我在大学毕业后工作的第一台计算机就是这样的机器(ICL Perq)。%p
的实现定义行为不满意,则可以使用C99 <inttypes.h>
和uintptr_t
。printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer);
0xA1B2CDEF
开头的特征下降出现,而不像0xa1b2cdef
那样在数字上下起伏。当GCC能够在编译时读取格式字符串时,(uintptr_t)
转换无疑是推荐的。我认为请求转换是正确的,尽管我相信有些人会忽略警告并大多数情况下逃脱惩罚。
Kerrek在评论中提问:
我有些困惑标准提升和可变参数。所有指针是否都会被标准提升为void *?否则,如果int *是2个字节,而void *是4个字节,那么从参数中读取四个字节显然是错误的,不是吗?
我曾经误以为C标准规定所有对象指针必须具有相同大小,所以void *和int *不能有不同的大小。然而,我认为C99标准的相关部分并没有如此强调(尽管我不知道在哪个实现中我所说的是真的或假的):
§6.2.5 类型 sizeof(int *) == sizeof(void *)
。好吧,SO很适合让您检查自己的假设。void *
不会改变其表示形式。由此转换得到的void *
值可以使用显式转换回原始函数指针类型,而不会丢失信息。printf()
)传递指针时,明确地将其转换为void *
是非常可取的,以实现最大的代码可靠性。在POSIX系统上,将函数指针转换为void指针以进行打印是安全的。在其他系统上,这样做不一定安全,也不一定安全地传递指针而不使用转换。p
是打印指针的转换说明符。使用它。
int a = 42;
printf("%p\n", (void *) &a);
记住省略类型转换是未定义行为,并且使用 p
转换说明符打印是以一种实现定义的方式进行的。
%p
表示指针类型,不要使用其他格式。标准并没有保证您可以将指针视为任何特定类型的整数,因此使用整数格式实际上会得到未定义的行为(例如,%u
需要一个无符号整数,但是如果 void*
具有与 unsigned int
不同的大小或对齐要求,该怎么办?)<inttypes.h>
中的指针特定宏。void*
,但是为了将指针作为可变参数传递,必须显式地进行转换(因为任意对象指针只能 可转换,而不能 等同于 void 指针)。printf("x lives at %p.\n", (void*)&x);
void *
(虽然对于printf()
函数,由于它是一个可变参数函数,你技术上需要显式转换)。函数指针不一定可以转换为void *
。 - cafvoid *
后再转回函数指针而不会丢失信息;幸运的是,POSIX明确要求这一点(并指出这不是标准C的一部分)。因此,在实践中,你可以这样做(将void (*function)(void)
转换为void *
,然后再转回void (*function)(void)
),但严格来说,这并不是C标准所规定的。 - Jonathan Leffler%u
和%lu
都是错误的。 printf
的规范非常清楚,当传递的类型与格式说明符所需的类型不匹配时,行为是未定义的。类型的大小是否匹配(这取决于机器)并不重要;必须匹配类型,并且它们永远不会匹配。 - R.. GitHub STOP HELPING ICE%u
需要一个 unsigned int
,而你正在传递一个 int *
,因此这是未定义行为。 - Kerrek SB uintptr_t
或 intptr_t
(来自 stdint.h
/ inttypes.h
),并使用相应的整数转换说明符。这样可以更灵活地格式化指针,但严格来说,实现不需要提供这些typedef。#include <stdio.h> int main(void) { int p=9; int* m=&s; printf("%u",m); }
使用 %u
格式说明符打印变量地址是否是未定义行为? 变量地址在大多数情况下是正数,所以我可以使用 %u
而不是 %p
吗? - Destructor你可以使用%x
或者%X
或者%p
,它们都是正确的。
%x
,地址将以小写字母形式给出,例如:a3bfbc4
%X
,地址将以大写字母形式给出,例如:A3BFBC4
这两种都是正确的。
如果你使用%x
或者%X
,它会考虑六个位置用于表示地址,而如果你使用%p
,它会考虑八个位置来表示地址。例如:
%X
是完全错误的。它期望无符号整数,如果系统的指针大小与整数大小相同,则可能会“工作”,但在今天的桌面系统上不是这样。正确的方法是使用格式%p
并将指针转换为(void*)
,因此您提供的任何解决方案都不正确。 - Weather Vane标准是%p
。
如果在64位系统上%x
只打印整个地址的部分,请改用%lx
或%llx
。
void*
吗?否则,如果int*
是两个字节,而void*
是4个字节,那么从该参数读取四个字节显然是一个错误,对吗? - Kerrek SBdlsym()
函数中。有一天我会写出这个变化......但是“有一天”不是“今天”。 - Jonathan Lefflervoid *
吗?嗯,我看到你在这里的评论(https://dev59.com/nmox5IYBdhLWcg3wqmBt#H4LinYgBc1ULPQZFsr8O)。由于只需要单向转换(函数指针到void *
),那么它就可以工作了? - chux - Reinstate Monicavoid *
并且再次转回时不会丢失信息。从实用角度来看,在很少的机器上,函数指针的大小与对象指针的大小不同。我认为,如果在这样的机器上打印函数指针,标准没有提供方法来解决这种转换问题。 - Jonathan Leffler