C ++ printf%p说明符在Linux和Windows中的行为有所不同

时间:2017-07-19 19:03:04

标签: c++ linux windows portability

我有一个C ++代码,可以在Linux和Windows上运行。

部分代码包括使用vsnprintf以使用格式字符串和参数获取字符串。

我注意到每当格式字符串包含%p时,Linux和Windows上的结果都不同 - Windows不会将结果前缀为0x,而Linux也会使用大写字母表示地址字母,而Linux使用小写字母。

我找不到标志来使两个版本完全相同。

我的偏好是使Linux版本的行为与Windows版本相同(因为Windows代码是原始代码,因此该程序应该如何表现)。

如果无法在Linux中更改vsnprintf的行为,我希望在vsnprintf输出后以一种有效的方式“修复”包含%p的字符串的方法。

2 个答案:

答案 0 :(得分: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

确切的格式取决于你。

答案 1 :(得分:1)

Justin's answer所示,您可以将指针转换为uintptr_t并根据需要对其进行格式化。这很可能是99%便携 - 而且由于你可能只关心Linux和Windows,这可能已经足够了。 (它可能在没有整数类型足以容纳指针值而不会丢失信息的系统上失败。这样的系统不会定义uintptr_t。你不太可能遇到这样的系统。)

另一种方法是使用%p格式将指针格式化为字符串,然后操纵结果字符串以获得所需的一致结果。创建一个带有void*参数并返回std::string的函数可能是有意义的。

这是我的尝试(我没有声称这是一个很好的C ++代码)。它会删除前导0x0X(如果存在),并将所有剩余字符映射为大写。

如果您没有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