【问题标题】:Why does a main function without a return statement return value 12?为什么没有return语句的main函数返回值12?
【发布时间】:2011-04-13 05:26:08
【问题描述】:

我编写了一个打印表格的程序。我没有在主函数中包含返回语法,但是每当我输入 echo $?它显示 12。

我的源代码:

#include <stdio.h>


int main(void)
{
    int ans,i,n;
    printf("enter the no. : ");
    scanf("%d",&n);

    for(i=1;i<=10;i++)
    {
        ans = n*i;
        printf("%d * %d = %d\n",n,i,ans);
    }
}

我没有写return 12,但是每次执行程序还是返回12。

谢谢。

【问题讨论】:

标签: c linux unspecified-behavior


【解决方案1】:

如果您完全熟悉汇编语言,您可能还记得函数的“返回值”是通过 EAX 寄存器传递的。

在这种情况下,返回值是从 EAX 中读取的。在这种情况下,恰好是 12。

但是,您并没有明确设置此值,它只是其余代码的产物(或者可能只是偶然)。

如前所述,这绝对是undefined behavior。如果你只是好奇为什么会这样,请考虑这个解释。

但在任何情况下都不要试图故意将此值用作任何有意义的东西。

【讨论】:

  • 这仅限于 x86 架构。它们可能是最常见的,但将其作为普遍事实肯定是不正确的。
  • 不,这不是未定义的行为。对于 C89,它是特定于实现的,对于 C99,必须返回 0
  • 在 ARM 上,r0 也可以这样说
  • @doron:这就是为什么在我看到这个答案之前发布的答案中(它们相隔不到一分钟),我使用了通用短语“用于返回值的寄存器”。在 x86 上是 EAX,在 ARM 上是 r0,并且大多数其他架构都有一个类似地列在 ABI 中的寄存器,用于指定返回值。
【解决方案2】:

正如 swegi 所说,这是未定义的行为。 正如 Steve Jessop 等人所说,在 C89 之前它是一个未指定的值,并在 C99 中指定(观察到的行为不符合 C99)

在大多数环境中实际发生的情况是最后一个printf 的返回值留在了用于返回值的寄存器中。

所以 n == 0 时为 11,如果 n 为一位数,则为 12,两位数 n 为 14,三位数 n 为 16,等等。

【讨论】:

  • 实际上是未指明的行为。
  • +1:不错的平台中立答案(谁有真正的基于堆栈的机器?;))
  • (-1,让我们看看)因为它不是未定义的行为,但在 C89 中它是特定于实现的,并且在 C99 中观察到的行为显然不符合要求。 (+1 可以很好地解释正在发生的事情,你很幸运)
  • 嘿,我从来不喜欢这个,很好
  • 术语“实现定义”要求每个实现都记录行为。在这种情况下没有这样的要求。
【解决方案3】:

您的程序在应该返回的时候没有返回任何东西,从而导致了未定义的行为,因此调用者通常会在过程返回时获取 eax 寄存器(在 x86 上,rax 在 x64 上)的值,即通常从最后一次使用 eax(通过函数返回值或仅基于寄存器的变量)中产生垃圾,在这种情况下,它可能是 printf 已写入标准输出缓冲区的 char 的数量

【讨论】:

    【解决方案4】:

    回答是因为所有现有的答案都说这是未定义的行为,这是不正确的,所以我没有什么可以投票的。

    在 C89 中(感谢 pmg 对 draft standard 的引用),5.1.2.2.3:

    从初始调用返回 主要功能是 相当于用值调用退出函数 由 main 函数作为其参数返回。如果 } 达到终止主要功能, 返回到宿主环境的终止状态是 未指定。

    在 C99 中,引用 n1256, 5.1.2.2.3:

    如果 main 的返回类型 函数是一种兼容的类型 int,从初始调用返回 主要功能相当于 调用退出函数 主函数返回的值为 它的论点;到达 } 终止主函数返回一个 值为 0。如果返回类型不是 与 int 兼容,终止 状态返回给主机 环境未指定。

    因此,这不是“未定义的行为”:它的行为就像 main 函数返回一样,但在 C89 中,返回的值未由标准指定。对于您的示例程序,在您的实现中,返回的值似乎始终为 12,这可能是 Ben Voigt 所说的原因。由于您使用的是 linux,因此将代码编译为 C99(或者无论如何,使用 gcc 几乎兼容的 C99 模式进行编译)可能并不是什么大变化。

    对于任何返回值的函数,除了main,它未定义的行为,除非调用者不使用返回值(n1256, 6.9 .1/12):

    如果终止函数的 } 是 达到,以及函数的值 call 被调用者使用, 行为未定义。

    我不确定是否应该提及对main 的初始调用作为排除在此一般规则之外。不需要是:从标准的POV来看,那个调用没有调用者,所以我认为函数调用的值不是“被调用者使用”,即使它变成了终止状态为程序。

    【讨论】:

    • 实际的 1990 ISO C 标准(可能是 1989 ANSI 标准)说“返回到主机环境的终止状态是未定义的”。显然,在草案和标准发布之间,该词已从“未指定”更改为“未定义”。顺便说一句,that link appears to be dead.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-06-12
    • 1970-01-01
    • 2018-11-20
    • 1970-01-01
    • 2022-12-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多