【问题标题】:C : Printing big numbersC:打印大数字
【发布时间】:2009-09-06 14:42:57
【问题描述】:

采取以下措施:

#include <stdio.h>

main() {
    unsigned long long verybig = 285212672;

    printf("Without variable : %llu\n", 285212672);
    printf("With variable    : %llu", verybig);
}

这是上面程序的输出:

Without variable : 18035667472744448
With variable    : 285212672

从上面可以看出,当printf 将数字作为常量传递时,它会打印出一些巨大的错误数字,但是当值第一次存储在变量中时,printf 会打印出正确的数字。

这背后的原因是什么?

【问题讨论】:

    标签: c printf


    【解决方案1】:

    试试285212672ULL;如果你写它没有后缀,你会发现编译器把它当作一个常规整数。它在变量中工作的原因是整数在赋值中被转换为unsigned long long,因此传递给printf() 的值是正确的类型。

    在你问之前,不,编译器可能不够智能printf() 格式字符串中的"%llu" 中找出它。这是不同的抽象级别。编译器负责语言语法,printf() 语义不是语法的一部分,它是一个运行时库函数(与您自己的函数没有什么不同,只是它包含在标准中)。

    考虑以下代码用于 32 位 int 和 64 位 unsigned long long 系统:

    #include <stdio.h>
    
    int main (void) {
        printf ("%llu\n",1,2);
        printf ("%llu\n",1ULL,2);
        return 0;
    }
    

    哪个输出:

    8589934593
    1
    

    在第一种情况下,两个 32 位整数 1 和 2 被压入堆栈,printf() 将其解释为单个 64 位 ULL 值,2 x 232 + 1 . 2 参数无意中包含在 ULL 值中。

    在第二个中,您实际上推送了 64 位 1 值和一个多余的 32 位整数 2,它被忽略了。

    请注意,格式字符串和实际参数之间的这种“脱节”是一个坏主意。比如:

    printf ("%llu %s %d\n", 0, "hello", 0);
    

    可能会崩溃,因为 32 位 "hello" 指针将被 %llu 消耗,%s 将尝试取消引用最终的 0 参数。下面的“图片”说明了这一点(假设单元格是 32 位的,并且“hello”字符串存储在 0xbf000000。

    What you pass     Stack frames     What printf() uses
                     +------------+
    0                | 0          | \
                     +------------+  > 64-bit value for %llu.
    "hello"          | 0xbf000000 | /
                     +------------+
    0                | 0          |    value for %s (likely core dump here).
                     +------------+
                     | ?          |    value for %d (could be anything).
                     +------------+
    

    【讨论】:

    • 但我认为编译器足够聪明,可以在 printf 格式规范中找出 %u,尝试 printf("%d %u",~0,~0).. 两者都会按预期打印值。 .
    • 不 - 这些数据类型的大小相同 - 它是 printf() 解决这个问题 - 尝试 %d 和 'a'。
    • Pax:这也很好,字符文字是整数常量。
    • 没错,你是@caf,但编译器仍然没有查看 printf 参数。它不应该,因为这是不同的抽象级别(语言与运行时库)。我已经用更多信息更新了答案。
    • 在代码中使用 1 和 2,但在文本中使用 0 和 1 :)
    【解决方案2】:

    值得指出的是,一些编译器会针对这种情况给出有用的警告 - 例如,GCC 对您的代码是这样说的:

    x.c: In function ‘main’:
    x.c:6: warning: format ‘%llu’ expects type ‘long long unsigned int’, but argument 2 has type ‘int’
    

    【讨论】:

    • 另一个不忽略编译的原因警告
    【解决方案3】:

    285212672 是一个 int 值。 printf 需要 unsigned long long,而您将传递给它 int。因此,它将从堆栈中取出比您传递实际值更多的字节并打印垃圾。当您在将其传递给函数之前将其放入 unsigned long long 变量中时,它将在赋值行中提升为 unsigned long long ,然后您将该值传递给 printf ,它可以正常工作。

    【讨论】:

      【解决方案4】:

      数据类型只是解释内存位置内容的一种方式。
      在第一种情况下,常量值作为 int 存储在只读内存位置中,printf 尝试将此地址解释为 8 字节位置,因为它被指示存储的值很长,在它打印垃圾值的过程中。
      在第二种情况下,printf 尝试将 long long 值解释为 8 个字节,并打印预期的内容。

      【讨论】:

        猜你喜欢
        • 2018-08-27
        • 1970-01-01
        • 2015-03-12
        • 1970-01-01
        • 2015-03-16
        • 2015-02-10
        • 1970-01-01
        • 2013-05-03
        • 2016-03-25
        相关资源
        最近更新 更多