【问题标题】:What is the output of this program?这个程序的输出是什么?
【发布时间】:2009-10-07 10:05:53
【问题描述】:

当我尝试在一个简单的 c 文件中运行编译并执行此语句时:

main(){ printf("%d");}

在 HP 上给我 64,在 AIX 上给我 804359524。

谁能告诉我这是什么行为。

【问题讨论】:

    标签: c++ c linux unix


    【解决方案1】:

    我假设你的意思是:

    int main()
    {
      printf("%d");
    }
    

    在这种情况下,printf() 正在从堆栈中读取一个 int(按照格式说明符 %d 的指示)。由于您没有指定一个,它只是读取堆栈上的任何内容并使用它。因此,您看到的伪随机输出。

    请尝试:

    int main()
    {
      printf("%d", 10101);
    }
    

    HTH

    【讨论】:

      【解决方案2】:

      这是经典的undefined behavior。编译器不会检查您是否提供了足够的参数来匹配您的格式化字符串。有编译器可以做到这一点 (gcc is one),但你的没有。

      printf() 中的代码将愉快地遍历其给定的格式化字符串,当它到达 "%d" 时,它将读取一个 int 大小的参数(通常:从堆栈中),不知道那里没有任何论据可供阅读。

      堆栈上的任何值都会被打印出来。

      【讨论】:

        【解决方案3】:

        如果您指定足够高的警告级别,某些编译器(如 gcc)会捕获这种常见的问题。像这样(编译你的代码,带有 -Wall - 所有警告):

        gcc    -c -g -Wall -MMD -MP -MF build/Debug/Cygwin-Windows/newmain.o.d -o build/Debug/Cygwin-Windows/newmain.o newmain.c
        newmain.c: In function `main':
        newmain.c:16: warning: too few arguments for format
        

        这是始终以高警告级别编译并认真对待警告消息的大约 998 个充分理由之一。

        【讨论】:

          【解决方案4】:

          代码暴露了未定义的行为。在其他系统上,它可能会打印“堆栈溢出”,(说真的!)。 %d 告诉 CRT 库您将提供一个整数,但您没有。

          【讨论】:

          • 我认为您不会在该代码中遇到堆栈溢出 - 堆栈没有增长。
          • 堆栈溢出的触发器通常是对堆栈保护页面的内存访问。 printf() 可以在读取 %d 整数失败时触发该访问,而是点击保护页。而且由于不是写,所以不能通过添加另一个页面来解决。每当存在任何不可修复的堆栈保护页违规时,操作系统打印“堆栈溢出”是合理的;这将是 99% 正确
          • 我认为在这种情况下,根据编译器的实现和它运行的操作系统。我一直认为“堆栈溢出”是指尝试在堆栈上存储数据时堆栈内存不足,而不是在读取数据时。
          • 如果我们假设 C ABI(参数从右到左压入堆栈)。 Skizz 是正确的,不太可能产生堆栈溢出(更可能是返回地址)。但这是一个很大的假设。
          • 不是一个大的假设,问题被标记为 C 和 C++。
          【解决方案5】:

          我的猜测是你编译

          main(){ printf("%d");}
          

          这将从当前堆栈中选择一个随机值。试试这个:

          main() {
             printf("%d", 0);
             printf("%d");
          }
          

          现在,第二个printf() 将始终打印0,因为它与第一个调用获得相同的堆栈。

          [编辑] 这不适用于带有 GCC 4.1.2 的 x86 Linux。这是生成的汇编代码(使用gcc -S -o t.s t.c):

              movl    $0, %esi
              movl    $.LC0, %edi
              movl    $0, %eax
              call    printf
              movl    $.LC0, %edi
              movl    $0, %eax
              call    printf
          

          如您所见,第二个参数没有被压入堆栈,而是通过%esi(这是一个寄存器)传递。同一个寄存器可能在printf() 中被修改,这就是它失去价值的原因。该死的优化;)

          【讨论】:

          • 如果你得到 1,我不会感到惊讶。 printf 返回消耗的参数数量。此返回值可能会覆盖堆栈。
          • 我怀疑这有两个原因:a)在大多数机器上,返回值在寄存器中,b)如果格式字符串没有保存,它会覆盖在堆栈上传递的格式字符串ABI 在不同的地方。
          • Aaron,使用 intel icc 编译器运行它会给出关于第二个 printf 的警告和 04204818(0 和 4204818)的输出
          • @Glen:奇怪。第二个 printf() 应该使用与第一个相同的堆栈地址,我认为没有理由覆盖第二个参数:/您是否有“mungwall”选项处于活动状态或有什么东西会破坏未使用的内存?
          • @AAron,没有打开任何特殊选项。 ">icc x.cc" 其中 x.cc 就是上面的代码。
          猜你喜欢
          • 2022-11-16
          • 1970-01-01
          • 1970-01-01
          • 2021-09-26
          • 1970-01-01
          • 1970-01-01
          • 2017-09-26
          • 2021-11-12
          • 2021-09-29
          相关资源
          最近更新 更多