Answers:
假设您不介意不同平台之间格式的变化和变化,最简单的答案就是标准%p
符号。
C99标准(ISO / IEC 9899:1999)在§7.19.6.1¶8中表示:
p
参数应为的指针void
。指针的值以实现定义的方式转换为一系列打印字符。
(在C11中-ISO / IEC 9899:2011中-信息在§7.21.6.1¶8中。)
在某些平台上,它将包含一个前导,0x
而在其他平台上则不包含,一个字母可以是小写或大写,并且C标准甚至没有定义它应为十六进制输出,尽管我知道没有实现就没有实现。
是否应使用强制转换显式转换指针尚有争议(void *)
。它是明确的,通常很不错(所以这就是我的工作),并且标准说“参数应该是指向的指针void
”。在大多数计算机上,您将无需显式转换。但是,在机器上,char *
给定存储位置的地址的位表示形式与同一存储位置的“ 其他任何指针 ”地址不同,这将很重要。这将是字寻址的机器,而不是字节寻址的机器。这些机器如今并不常见(可能不可用),但是我大学毕业后研究的第一台机器就是这样的机器(ICL Perq)。
如果你不满意的实现定义%p
,然后用C99 <inttypes.h>
和uintptr_t
替代:
printf("0x%" PRIXPTR "\n", (uintptr_t)your_pointer);
这使您可以微调表示以适合自己。我选择将十六进制数字大写,以使数字一致地具有相同的高度,并0xA1B2CDEF
因此出现在开始时的特征倾角,而不是像0xa1b2cdef
沿数字那样上下倾角。但是,您可以在非常广泛的范围内进行选择。该(uintptr_t)
投是通过明确推荐GCC时,它可以读取在编译时的格式字符串。我认为请求强制转换是正确的,尽管我敢肯定有些人会忽略警告并在大多数情况下逃避它。
Kerrek在评论中要求:
我对标准的促销活动和各种各样的争论感到困惑。所有指针是否都标准提升为void *?否则,如果
int*
说是两个字节,又void*
是4个字节,那么从参数中读取四个字节显然是错误的,不是吗?
我是C标准说,所有的对象指针必须是相同的大小,所以这样的误解,void *
而int *
不能是不同的尺寸。但是,我认为C99标准的相关部分并没有那么强调(尽管我不知道我建议为true的实现实际上是false的实现):
§6.2.5类型
¶26指向void的指针应与指向字符类型的指针具有相同的表示和对齐要求。39)同样,指向兼容类型的合格或不合格版本的指针应具有相同的表示和对齐要求。所有指向结构类型的指针应具有相同的表示和对齐要求。所有指向联合类型的指针应具有相同的表示和对齐要求。指向其他类型的指针不必具有相同的表示或对齐要求。
39)相同的表示形式和对齐要求旨在表示与函数的参数,函数的返回值以及并集的成员具有互换性。
(C11在第6.2.5节,第28节和脚注48中说的完全相同。)
因此,所有指向结构的指针都必须具有相同的大小,并且必须共享相同的对齐要求,即使指针指向的结构可能具有不同的对齐要求。工会也是如此。字符指针和空指针必须具有相同的大小和对齐要求。int
(含义unsigned int
和signed int
)的变体指针必须彼此具有相同的大小和对齐要求;对于其他类型也是如此。但是C标准没有正式这么说sizeof(int *) == sizeof(void *)
。哦,很适合让您检查自己的假设。
C标准明确要求函数指针的大小与对象指针的大小相同。不必破坏类似于DOS的系统上的不同内存模型。那里可能有16位数据指针,但有32位函数指针,反之亦然。这就是为什么C标准不要求可以将函数指针转换为对象指针,反之亦然的原因。
幸运的是(对于面向POSIX的程序员而言),POSIX闯入了漏洞并确实要求函数指针和数据指针的大小相同:
§2.12.3 指针类型
所有函数指针类型都应具有与void的类型指针相同的表示形式。将函数指针转换为
void *
不得改变表示形式。void *
可以使用显式强制转换将这种转换产生的值转换回原始函数指针类型,而不会丢失信息。注意:ISO C标准不需要这样做,但是为了符合POSIX要求,它是必需的。
因此,void *
在将变量传递给诸如的可变参数函数的指针时,似乎强烈建议显式强制转换为最大的代码可靠性printf()
。在POSIX系统上,将函数指针强制转换为空指针以进行打印是安全的。在其他系统上,这样做不一定是安全的,传递指针而不是强制转换也不一定安全void *
。
dlsym()
功能。有一天我会写下更改...但是“一天”不是“今天”。
void *
和反向转换而不会丢失信息。实用上,很少有机器的函数指针的大小与对象指针的大小不同。我认为该标准没有提供在转换有问题的机器上打印功能指针的方法。
p
是打印指针的转换说明符。用这个。
int a = 42;
printf("%p\n", (void *) &a);
请记住,省略强制类型p
转换是未定义的行为,并且使用转换说明符进行的打印是以实现定义的方式进行的。
将%p
用作“指针”,请勿使用其他任何*。该标准不能保证允许您将指针像任何特定类型的整数一样对待,因此使用整数格式实际上会获得未定义的行为。(例如,%u
期望使用unsigned int
,但如果void*
尺寸或对齐要求与unsigned int
?不同,该怎么办?)
*)[请参阅Jonathan的最佳答案!]除了%p
,您还可以使用<inttypes.h>
C99中添加的的指针专用宏。
所有对象指针都可以void*
在C 中隐式转换为,但是为了将指针作为可变参数传递,您必须显式转换它(因为任意对象指针只能转换,但与 void指针不同):
printf("x lives at %p.\n", (void*)&x);
void *
(尽管从printf()
技术上讲,您需要显式强制转换,因为它是可变参数)。函数指针不一定可以转换为void *
。
void *
不损失的情况下转换为函数指针,也可以转换回该函数指针。不过,幸运的是,POSIX确实明确要求(请注意,它不是标准C的一部分)。因此,在实践中,你可以摆脱它(转换void (*function)(void)
到void *
和回void (*function)(void)
),但严格说来,这不是由C标准规定。
%u
!
%u
并且%lu
在所有机器上都是错误的,而不是某些机器。的规范printf
非常清楚,当传递的类型与格式说明符要求的类型不匹配时,行为是不确定的。类型的大小是否匹配(取决于机器,是真还是假)无关紧要;这是必须匹配的类型,而它们永远不会匹配。
作为其他(非常好)答案的替代方法,您可以强制转换为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
吗?
%u
是unsigned int
类型的格式,不能与的指针参数一起使用printf
。
void*
?否则,如果int*
说是两个字节,又void*
是4个字节,那么从参数中读取四个字节显然是错误的,不是吗?