【问题标题】:Why can one printf() in C not print two 64-bit values at the same time?为什么 C 中的一个 printf() 不能同时打印两个 64 位值?
【发布时间】:2011-07-27 20:21:20
【问题描述】:

我正在开发一个 32 位系统。当我尝试在单个 printf 中打印多个 64 位值时,它无法再打印任何其他(即第 2、第 3、...)变量值。

示例:

uint64_t a = 0x12345678;
uint64_t b = 0x87654321;
uint64_t c = 0x11111111;

printf("a is %llx & b is %llx & c is %llx",a,b,c);

为什么这个 printf 不能打印所有值?

我正在修改我的问题

printf("a is %x & b is %llx & c is %llx",a,b,c);

这样做的结果是:a 是 12345678 & b 是 8765432100000000 & c 是 1111111100000000

如果我没有正确打印 a 的值,那么为什么其他的值会发生变化??

【问题讨论】:

  • 您得到的输出(或错误)是什么?
  • 其实可以的:ideone.com/um0QL
  • 您不能将ll 用于uint64_tll 只能用于 long long 类型。你应该使用<inttypes.h> macros!
  • 当我将您的代码 sn-p 包含在一个完整的程序中时,它会产生预期的输出。请提供一个最小的、完整的示例程序以及您的预期输出和您实际看到的输出。有关创建最小完整程序的人员以及为什么这是一个有用的工具的说明,请参阅sscce.org
  • 您使用的是什么编译器、操作系统和编译器选项?

标签: c printf


【解决方案1】:

你应该使用<inttypes.h>中定义的宏

printf("a is %"PRIx64" & b is %"PRIx64" & c is %"PRIx64"\n",a,b,c);

它丑得要命,但它很便携。这是在 C99 中引入的,因此您需要一个兼容 C99 的编译器。

【讨论】:

  • 既然这被接受了,我想知道PRIx64 在Xcode 4.1 上的格式是什么,如果它不是“llx”?
  • 我无权访问该编译器,但如果有,您可以尝试:printf("PRIx64 is %s\n", PRIx64") 或更短的printf("PRIx64 is "PRIx64"\n");
  • 您坚持PRIx64 等是完全正确的。但我对OP 的不同之处的猜测只是格式字符串末尾的\n...
  • @Jens 你可能是对的:P 我将它添加到我的答案中甚至没有考虑到这一点,我想这只是反射:)
  • 在 Xcode 4.1 中,PRIx64 是 #define PRIx64 __PRI_64_LENGTH_MODIFIER__ "x"#define __PRI_64_LENGTH_MODIFIER__ "ll"
【解决方案2】:

你需要使用正确的格式:

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>

int main(void)
{
    uint64_t a = 0x12345678;
    uint64_t b = 0x87654321;
    uint64_t c = 0x11111111;

    printf("a is %#" PRIx64
            " & b is %#" PRIx64
            " & c is %#" PRIx64 "\n",
            a, b, c);
    return EXIT_SUCCESS;
}

输出:

a is 0x12345678 & b is 0x87654321 & c is 0x11111111

【讨论】:

  • printf("a 是 %x & b 是 %llx & c 是 %llx",a,b,c);通过这样做,结果是:a 是 12345678 & b 是 8765432100000000 & c 是 1111111100000000 如果我没有正确打印 a 的值,那么为什么其他的值会发生变化??
  • 我收到警告使用 %#" PRIx64 "..." 所以我只是删除了 # 并且它工作正常。(msys2 gcc 5.3.0)
  • # 做什么(如%#)?
【解决方案3】:

它将它们全部打印在我的计算机上,但是由于 %llx 需要 long long unsigned int,因此出现了三个编译时警告。

您确定需要使用 64 位类型吗?您的所有三个十六进制代码都只有 32 位。也许您可以只使用 32 位并执行以下操作:

unsigned int a = 0x12345678;
unsigned int b = 0x87654321;
unsigned int c = 0x11111111;

printf("a is %x & b is %x & c is %x",a,b,c);

(或使用 32bit unsigned int 的 stdint 等价物)

除非您需要它们为 64 位,以便以后可以向它们添加更多位。

【讨论】:

    【解决方案4】:

    对于名为 Stack Overflow 的论坛来说,意外的printf() 输出的原因是堆栈未对齐。 %x 转换规范和函数参数 a 之间的大小不匹配导致不对齐。

    编译语句时

    printf("a is %x & b is %llx & c is %llx",a,b,c);
    

    编译器生成机器码,将函数参数按从右到左的顺序压入堆栈。

    编译器使用变量声明而不是格式字符串来确定堆栈上每个参数的数据大小(可能用于生成警告除外)。在 x86 CPU(如在大多数机器中)中,堆栈指针随着每次推送而递减。当进入printf()库函数时,栈因此有如下布局:

    00A4:... 00A0: 00000000 009C: 11111111 变量 'c' 作为 uint64 压入堆栈 0098: 00000000 0094: 87654321 'b' 作为 uint64 推入堆栈 0090: 00000000 008C: 12345678 'a' 作为 uint64 推入堆栈 0088: 0084:

    对于本例来说,0084 的栈顶地址是任意的。

    由于所有三个变量都声明为uint64_t,因此编译后的代码会将这些变量作为 64 位值推入堆栈。对于 x86 CPU 等小端机器,每个 uint64 值的高字节最终位于较高的地址中。

    printf() 的实现使用格式字符串来确定堆栈上参数的数量和大小。与编译器不同,printf() 不接收有关原始变量声明的信息。第一个转换规范是%x,所以printf() 期望a 是一个32 位的值,因此printf() 解析堆栈布局如下:

    00A4:... 00A0: 00000000 009C:11111111 0098: 00000000 '%llx' 将 'c' 读取为 uint64,但地址错误 0094:87654321 0090: 00000000 '%llx' 将 'b' 读取为 uint64,但地址错误 008C: 12345678 '%x' 导致 printf() 将 'a' 读取为 uint32 0088: 0084:

    堆栈未对齐解释了为什么 a 会按预期打印 12345678,但 bc 已被有效地左移 32 位,分别为 8765432100000000 和 1111111100000000。

    更正第一个 %x 转换规范或将参数 a 转换为 uint32 应该可以解决问题。

    【讨论】:

      【解决方案5】:

      用途:

      "%lli" for int64_t
      "%llu" for uint64_t
      "%llx" for hex
      "%llX" for HEX
      

      查看“inttypes.h”内部。

      【讨论】:

        猜你喜欢
        • 2017-06-02
        • 2017-08-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多