printf("%llx") 有什么问题?

14

我有一段代码,它挑战了我对C语言的所有知识。

这是代码:

int main(void){
    unsigned long long int massage ;

    scanf("%llX", &massage); //input: 0x1234567890abcdef
    printf("%llX", massage);
    return 0;
}

在我的 "64位-Corei5-Fedora-GCC" 上,它输出我所输入的内容。但是在我的朋友的系统上(32位,MS XP,MinGW),它输出 90ABCDEF。我不明白为什么,有人知道吗?

顺便说一下:unsigned long long intsizeof 在他的系统上是8。


3
他是在用MSVC或者其他编译器吗?它们有非标准的printfscanf格式。应该可以在编译器的文档中找到相关信息。 - Daniel Fischer
他使用MINGW。 - Untitled
2
MinGW使用自己的C库,还是使用Windows自带的C库?在后一种情况下,非标准格式仍然适用。在前一种情况下,则不得而知。 - Daniel Fischer
1
@DanielFischer 相关链接:https://dev59.com/ZmYr5IYBdhLWcg3wi6zd 显然,它使用的是Windows的链接库。 - user529758
2个回答

12
问题在于编译器相信的内容(如反映在 sizeof 中: sizeof(unsigned long long int) 在编译时求值)与运行时库所相信的内容不一致(如反映在 printf 中: printf 函数在运行时调用,因此它的格式说明符是在运行时生效的)。
根据 MinGW 文档中的 “C99” 介绍:
引用:

GCC 不包含 C 运行时库。这由平台提供。GCC 的 MinGW 版本使用 Microsoft 原始(旧版)Visual C 运行时库 MSVCRT,该库是针对 Microsoft Visual Studio 6(发布于1998年)而定位的。

[...]

由于 MinGW 依赖 MSVCRT,它具有与 Visual Studio 6 兼容性相同的许多限制和怪癖。您应该假设 MinGW 应用程序不能依赖于 C99 行为,只能依赖于 C89。例如,不支持 printf 中的新格式字符 %a 和 %ll,尽管存在一个 %ll 的解决方法

(它提到的解决方法是使用代替 llI64:因此,使用 %I64X。令人恼火的是,至少在我的系统上,当它在文字格式字符串中看到这个时,GCC 会发出警告,因为它认为它将有一个更好的运行时库。)

1
仍然很好让编译器检查格式字符串。毕竟,格式字符串的正确性取决于printfscanf的其他参数,因此在某种程度上,它是函数的一部分。您可以创建预处理器常量HEX64,它在MSVCRT版本中转换为"%I64X",在类POSIX版本中转换为"%llX"。然后,您可以编写 printf("I like the number " HEX64 "!", number) ,GCC仍然可以检查number的类型是否与格式字符串匹配。 - user824425
1
@Tinctorius:抱歉,我觉得我们之间出现了误解。我的意思是,当我在使用MinGW的Windows系统上使用带有“%I64X”的字面字符串时,GCC会给我一个警告消息,因为它不认识那个符号。它根本不知道运行时库的任何信息,所以它假设是POSIX-y的。也就是说,即使我在一个POSIX符号无法工作的系统上,GCC的警告仍然试图让我使用POSIX符号。这是有道理的,但很烦人。 - ruakh
不需要担心这个问题。MinGW在其inttypes.h的副本中提供了一组与Windows兼容的C99 printf类型说明符定义,因此如果您使用符合C99的说明符,则可以避免这些问题。不要使用printf("%llx\n", foo),而是#include <inttypes.h>并使用printf("%" PRIx64 "\n", foo);MinGW将使其成为%I64x,UNIX类平台将使其成为%llx。您可能还需要向GCC传递--std=gnu99 - Jody Bruchon
@JodyLeeBruchon:谢谢!你知道那是什么时候添加的吗?也就是说,除了尝试外,一个人如何轻松地确定它是否适用于给定的系统? - ruakh
使用-D__USE_MINGW_ANSI_STDIO=1来使用真正的stdio,而不是糟糕的Windows版本。 - Shahbaz
显示剩余4条评论

4

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