C++ printf %p 说明符在 Linux 和 Windows 中的行为不同
Posted
技术标签:
【中文标题】C++ printf %p 说明符在 Linux 和 Windows 中的行为不同【英文标题】:C++ printf %p specifier behaves differently in Linux and Windows 【发布时间】:2017-07-19 19:03:04 【问题描述】:我有一个 C++ 代码,可以在 Linux 和 Windows 上运行。
部分代码包括使用vsnprintf
以使用格式字符串和参数获取字符串。
我注意到,每当格式字符串包含 %p 时,Linux 和 Windows 上的结果是不同的 - Windows 不会在结果前面加上 0x 而 linux 会这样做,而且 Windows 使用大写的地址字母而 Linux 使用小写.
我找不到使两个版本相同的标志。
我的偏好是让 Linux 版本的行为类似于 Windows 版本(因为 Windows 代码是原始代码,所以这就是程序预期的行为方式)。
如果无法在 Linux 中更改 vsnprintf
的行为,我想要一种方法来“修复”在 vsnprintf
输出后包含 %p 的字符串(以一种有效的方式)。
【问题讨论】:
%p
的行为是implementation defined
@Borgleader:不是。 0x%X
需要 unsigned int
类型的参数;给它一个指针具有未定义的行为。如果unsigned int
和指针大小相同(它们通常不是),它可能会起作用;这始终是未定义行为的可能结果。
您的问题被标记为 C 和 C++。它们是两种不同的语言。你用的是哪一个? (在极少数情况下,您可能希望编写在两种语言中都能正常工作的代码;如果是这样,请在问题中说明。)
@KeithThompson 我已经更新了这个问题。它在 C++ 中
@Justin 没有说明如何将指针转换为uintptr_t
。只有与这些之间的转换才能保证工作。
【参考方案1】:
%p
打印的字符串是implementation defined。这就是 Linux 和 Windows 上的行为不同的原因。如果您想要一致的行为,则必须实现自己的版本。
使用uintptr_t
,我们可以得到一个可以容纳指针的整数。所以我们可以将reinterpret_cast
指针插入其中。请注意,虽然转换会成功,但没有指定值将保持什么。
然后,我们可以使用std::hex
或适当的format macro constant 将整数打印为十六进制:
auto* myPointer = ...;
std::cout << std::hex << reinterpret_cast<std::uintptr_t>(myPointer) << '\n';
std::printf("%" PRIxPTR "\n", reinterpret_cast<std::uintptr_t>(myPointer));
Demo
具体格式由您决定。
【讨论】:
我想尝试这个解决方案,但我需要能够在运行时以编程方式检测格式字符串中%p
的每次出现,并将其替换为PRIXPTR
。我怎样才能做到这一点? (检测很容易,但我如何用宏替换字符串p
?
@darkThoughts 请注意,对于字符串文字,格式宏常量是 #define
s。比如在coliru上,PRIxPTR
和"lx"
是一样的(当然不同平台实际值会有所不同)。所以你可以找到"%p"
的出现并用"%" PRIxPTR
替换它们(在coliru 上,它相当于"%lx"
)
但由于定义依赖于平台,我不能只用"%X"
或"%llX"
静态替换出现的"%p"
- 我必须用定义替换它们。我该怎么做?
@darkThoughts "some string with %p in the middle"
-> "some string with %" PRIXPTR " in the middle"
我该如何做这个替换 - 我需要在 p
周围拆分原始字符串吗?【参考方案2】:
正如Justin's answer 建议的那样,您可以将指针转换为uintptr_t
并根据需要对其进行格式化。这可能是 99% 的可移植性——而且由于您可能只关心 Linux 和 Windows,这可能已经足够好了。 (在没有足够大的整数类型来保存指针值而不丢失信息的系统上,它可能会失败。这样的系统不会定义uintptr_t
。你不太可能遇到这样的系统。)
另一种方法是使用%p
格式将指针格式化为字符串,然后操作生成的字符串以获得所需的一致结果。创建一个接受void*
参数并返回std::string
的函数可能是有意义的。
这是我的尝试(我没有声称这是好的 C++ 代码)。它会删除前导 0x
或 0X
(如果存在),并将所有剩余字符映射为大写。
如果您没有 C++11 编译器,请调整 for
循环。
#include <iostream>
#include <string>
#include <cstdio>
#include <cctype>
std::string hex(void* ptr)
const int big_enough = 100;
char s[big_enough];
std::snprintf(s, sizeof s, "%p", ptr);
std::string result = s;
std::string prefix = result.substr(0, 2);
if (prefix == "0x" || prefix == "0X")
result = result.substr(2);
for (auto &&c : result)
c = std::toupper((unsigned char)c);
return result;
int main()
int n;
int *ptr = &n;
std::printf("Using %%p: %p\n", (void*)ptr);
std::cout << "Using hex(): " << hex((void*)ptr) << "\n";
我的 Linux 系统上的输出是:
Using %p: 0x7ffd6e8ca884
Using hex(): 7FFD6E8CA884
【讨论】:
以上是关于C++ printf %p 说明符在 Linux 和 Windows 中的行为不同的主要内容,如果未能解决你的问题,请参考以下文章