【问题标题】:Can printf result in undefined behavior? [duplicate]printf 会导致未定义的行为吗? [复制]
【发布时间】:2014-04-25 07:50:36
【问题描述】:
int main() 
{
  unsigned int i = 12;
  printf("%lu", i); // This yields a compiler warning
}

在 32 位平台上,使用 printf 和使用 %lu 的 int 会导致垃圾吗?

【问题讨论】:

    标签: c++ c


    【解决方案1】:

    仅声明“32 位平台”并不意味着 intlong 以及它们的 unsigned 对应物都具有 32 位。

    所以是的,如果 unsingned long%lu 的用途)比 unsigned int 长,确实会发生这种情况。

    但即使长度相等,类型也不兼容,所以形式上它是未定义的行为。

    【讨论】:

    • 如何模拟这种情况?
    • 你说得对 (-> +1 ) 但几乎所有当前的编译器都是如此。
    • LP64 (en.cppreference.com/w/cpp/language/types) 中,long 是 64 位,int 是 32 位。
    • LP64 不是 32 位平台。我从未见过 long 和 int 不是 32 位的 32 位平台,而且我的大部分工作都是在 32 位平台上进行的:Sparc、HP/PA、Power PC,甚至在过去几年中的一些 Intel。而且,在 32 位模式下,intlong 都是 32 位。
    • 这是错误的解释。该标准要求%luunsigned long 类型匹配。 unsigned longunsigned int 类型即使具有相同的宽度也不相同,即使它们传递一个给另一个也是未定义的行为。
    【解决方案2】:

    如果需要的类型和给定的类型不兼容,你 有未定义的行为。编译器完全合法 传递 vararg 时将类型信息与值一起传递,并且 在va_arg 中利用它(虽然我不知道有哪个, 可能是历史原因)。

    至于您的具体情况的实际效果,"%lu" 期望一个无符号长。唯一的其他类型是 兼容是长的,然后只有当实际值 long 是非负的。传递int 是未定义的 行为,尽管它可能有效。 (在大多数 32 位平台上, intlong 具有相同的大小和表示形式。)

    【讨论】:

    • 如果 int 是 4 字节,long 8 字节,这真的会导致未定义的行为吗?
    • @bryantism 结果确实是未定义的。 “看起来工作”是一种有效的未定义行为。但是,是的,在 intlong 具有不同表示的平台上,您更有可能看到明显奇怪的东西。
    • @bryantism 即使intlong 都是4 个字节,行为也未定义。如上所述,实现可以将类型信息与值一起传递,并在va_arg 中使用它来确保传递了正确的类型。
    • @BoBTFish 如果平台是 little endian,并且没有其他 varargs,它似乎仍然可以工作(当然,它仍然是完全未定义的行为)。
    【解决方案3】:

    老实说,我不明白为什么要使用 %lu 代替 %u,因为您使用的是 int。

    %lu 应该(在其非常基本的解释中)用于 unsigned long。

    如果您的编译器对 int 和 long 使用不同的存储大小(当然在 99% 的情况下会这样做),它很可能会打印垃圾。

    例如,根据 C 标准,就存储大小而言,unsigned int 是“至少 16 位大小”。而 unsigned long 是“至少 32 位大小”。

    现在,让我们以 16 位 int 和 32 位 long 为例,并考虑一个非典型示例,在您运行程序时内存全部归零。

    你的值 12 在内存中表示为:

    00000000 00001100

    如果你用 %u 打印会得到 12:

    如果指示 printf 打印为 %lu 会导致 printf 占用的内存为:

    00000000 00001100 00000000 00000000

    对应786432的long值

    编辑: 将变量的值传递给 printf 函数(而不是变量的指针(和大小))使代码无论如何都可以工作。我之前的“解释”主要是解释为什么提出警告以及为什么它“通常”是一种错误的方法。

    【讨论】:

    • 我不同意在 99% 的情况下,long 和 int 具有不同的大小。在我见过的所有 32 位平台上,它们的大小都相同。
    • 当然,关于printf 的问题更复杂,但在最常见的平台(基于英特尔)上,整数是小端的,所以按字节计算,他的 12 将是 0C 00(在16 位)或 0C 00 00 00(32 位)。
    • 谢谢,之前没在意这种问题,直到今天遇到这个问题,在生产代码调试日志中确实打印了一个垃圾值,可能是我们的gcc太老了……
    • James,例如在我的 I7,64 位 Linux 发行版上,int 的大小是 4,long 的大小是 8。老实说,我确认我之前所说的,大多数的时候他们是不同的。尝试制作一个简单的 sizeof(int) 和 sizeof(long) 会报告差异。
    • @MaurizioBenedetti 是的,64 位 Linux 通常有不同的 int 和 long 大小,但在 Windows 上,即使在 64 位下它们也是相同的。 OP 还专门讨论了 32 位平台,它们几乎在所有地方都是相同的。所以我不会说他们 99% 的时间都是不同的。
    猜你喜欢
    • 1970-01-01
    • 2014-08-11
    • 2016-09-04
    • 1970-01-01
    • 2013-04-24
    • 1970-01-01
    • 2016-02-17
    • 2022-11-16
    • 2017-11-22
    相关资源
    最近更新 更多