【问题标题】:Unexpected output in CC中的意外输出
【发布时间】:2014-10-24 02:22:13
【问题描述】:

以下是我的代码:

#include<stdio.h>

int i =5;

int main(i)
{
 if(i<10)
  printf(" %d\n",printf("%d",i+main(++i)));
 return 0;
}

输出

(在 Ideone.com 和 Codeblocks 中)

10 2

9 1

8 1

7 1

6 1

5 1

4 1

3 1

2 1

有人能解释一下这个输出背后的原因吗?我预计是 91, 81, ...., 51。另外,递归 main() 是否会导致意外输出?

PS:这是我在网上论坛上找到的一个程序。

【问题讨论】:

标签: c recursion


【解决方案1】:

首先,“隐式 int”规则很久以前就被取缔了。 int main(i) { ... 不是有效的函数声明。该代码无法在兼容的 C 编译器中编译。

其次,表达式i+main(++i) 没有充分排序。它会导致未定义的行为。在同一个表达式中读取变量i 并独立修改变量i 而不在这些操作之间进行适当的排序是非法的。在这种情况下,语言没有定义行为。

实际上,不知道i+main(++i)中的第一个i的值是在++i修改i的值之前还是之后读取。该语言对此不做任何保证。您以i 开头,等于1。对于i 的值,尚不清楚i+main(++i) 表达式的第一次评估是否将等效于1+main(2)2+main(2)。这就是你的实验中发生的事情。您出于某种原因假设第一个 i 将在 i 递增之前被读取。实际上,它以另一种方式起作用。

附:形式上,C 语言(相对于 C++)似乎允许手动调用 main。但是,您遇到的问题与 main 递归无关。

【讨论】:

  • @code_dweller:第二段说不知道++i是否会在i的第一次出现之前或之后更新i的值。 IE。不知道第一个递归表达式是否等同于5 + main(6)6 + main(6)。语言虽然只是说行为是未定义的。
  • @code_dweller 关联性与评估顺序不同。关联性从左到右,但未指定评估顺序。
  • @code_dweller:关联性和运算符优先级仅描述运算符及其操作对象之间的分组。关联性和运算符优先级与求值顺序 完全无关。仅仅因为某些运算符是左结合的,并不意味着它的操作数将从左到右进行计算。一点也不。
  • @code_dweller:好吧,我自己也搞糊涂了。全局i(以5 初始化)根本不在任何地方使用。所有计算均使用声明为main 的参数的local 变量i 执行。它实际上充当mainargc 参数。环境执行对 main 的第一次调用,i 设置为1,表示一个隐式程序参数(程序名称本身)。所以,i的起始值其实是1
  • @code_dweller:全局 i 被本地 i 隐藏(阴影)。你每次在main中说i,都是指本地的i。因此,不可能从main 引用全局i(实际上,这是可能的,但情况不同)。原始 ANSI C - C89/90 中允许“隐式 int”。显然您正在 C89/90 模式下运行编译器。
【解决方案2】:

谜题中有许多未定义的位,但printf 是输出的关键。其他人已经详细说明了i+main(++i)表达式的非法性,所以我们只看printf来分析输出:

printf(" %d\n",printf("%d",i+main(++i)));

有两个printf 语句,其中必须首先评估printf("%d",i+main(++i)) 以便为另一个提供输出。所以选择您的未定义值并将其插入%d。从这里开始定义行为。

man printf - 返回值:

Upon successful return, these functions return the number of characters printed

鉴于printf("%d",i+main(++i)) 在第一种情况下输出10,对于打印的两个字符,返回是22 的值被传递给 printf(" %d\n",..,第一次传递的输出为:

10 2

在第二遍中选择您未定义的行为,i+main(++i) 的值是 9printf("%d",i+main(++i)) 的返回现在是 1 给你的第二行输出:

 9 1

等等等等……我无法解释未定义的10, 9, 8, ...,但其余的都是有道理的。

【讨论】:

    【解决方案3】:

    main 的参数,变量 i,假定为 int,它在本地接管全局 i。作为 main 的参数,第一次调用它时,它的值是“参数数量”+1。因此,如果您在不带参数的情况下运行程序,则第一次调用 main 以 1 开头。尝试带一些参数运行您的程序,您会得到不同的结果。

    对我来说

    ./a.out a b c
    10 2
    9 1
    8 1 
    7 1
    6 1
    5 1
    

    理解评估的顺序很棘手。考虑以下程序及其输出,编译器在做什么(或不做什么)可能会变得更清楚。

    #include<stdio.h>
    
    int f(int x) {
      printf("f with x = %d\n", x);
      return x;  
    }
    
    int g(int x) {
      printf("g with x = %d\n", x);
      return x;  
    }
    
    int main()
    {
      int i;
    
      i = 0;
      printf("%d\n", f(++i) + i + g(++i));
    
      i = 0;
      printf("%d\n", (f(++i) + i) + g(++i));
    
      i = 0;
      printf("%d\n", f(++i) + (i + g(++i)));
    }
    

    输出

    f with x = 1
    g with x = 2
    4
    f with x = 1
    g with x = 2
    4
    f with x = 1
    g with x = 2 
    5
    

    【讨论】:

    • 很好的例子。第三个 printf() 的求值顺序是什么?
    猜你喜欢
    • 1970-01-01
    • 2021-10-13
    • 2020-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-07
    • 2013-09-08
    • 1970-01-01
    相关资源
    最近更新 更多