【问题标题】:Unspecified return values未指定的返回值
【发布时间】:2012-12-13 11:37:36
【问题描述】:

关于返回值:

func()
{
  while(1)
  {
    /* do stuff here */
    if(error1) exit(0);
    if(error2) break;
  }       
  /* no return statement anywhere in func() */
}

但是调用者检查了func()的返回码

if(func()) {/* error handling */}

如果有人确认func() 的返回值没有默认为任何值并且是垃圾,那最好。所有这些都是如此:

  • void func()
  • int func(),根本没有返回声明,或者是一个普通的return;
  • func(),未指定的返回类型,我理解默认返回 int。

谢谢..

【问题讨论】:

  • 另请注意,在 C 中定义或声明一个没有列出参数的函数意味着参数也未指定。如果一个函数不应该带参数,它必须明确定义/声明带void的参数。
  • 感谢@JoachimPileborg,我实际上是在那里发布一些东西来表达我的观点,但谢谢。

标签: c return return-value


【解决方案1】:

注意:此答案适用于 C89(及更早版本)。

void func() 指定一个不返回值的函数。在这种情况下,if (func()) 应该会导致编译错误。

int func()func() 是等价的并且返回一个整数值。 如果 return 语句未提供任何值,则结果未定义,如果警告级别足够高,编译器会发出警告。 在实践中,大多数编译器将生成代码,返回用于返回值的寄存器或内存位置中的任何内容。

【讨论】:

  • -1,隐式 int 无效 C,已从标准中删除。
  • 谢谢,对此添加了注释。
  • “结果未定义” - 准确地说,如果“调用者使用函数调用的值”,行为是未定义的。所以它不仅仅是“可能是任何整数,不知道是哪个”,而是“可能崩溃”。不过,我不确定什么调用约定实际上会导致崩溃。
  • @Steve。我的措辞选择不当。你是绝对正确的,无论程序做什么,它都不会破坏 C 规范。如果我曾经编写过 C 编译器,我会为每个调用的未定义行为添加彩蛋。我的意思是,如果你要失败,为什么不以时尚的方式去做呢?
【解决方案2】:

函数的返回值一般由处理器的第一个寄存器返回,例如基于 x86 的 EAX 或基于 ARM 的处理器的 r0。它是特定于编译器的,但这或多或少是标准的。

例如 C 中的return 10; 将被翻译成

mov eax 10 ; pseudo code
ret        ; pseudo code

在基于 x86 的处理器的汇编中。

& 在检查返回值时,它只会检查 eax 寄存器。该寄存器中包含的任何内容都被调用者视为返回值。

所以...如果您返回时没有值(简单的return;)或者它是一个 void 函数,那么在汇编中 ret 语句之前的 EAX 中包含的任何值都会被调用者作为返回值。

对于基于 ARM 的处理器,将上述答案中的 EAX 替换为 r0。 :-)

我将此事实用作以下功能的 hack ;-)

void* getStackTop(){
   asm("mov eax esp");
}

要检查从 C 转换为 ASM 时它的工作原理,请编写如下简单代码:

int testfn(int unused){
   int unused2=unused; // Check how input is passed
   return 10;          // Check how output is returned
}

& 然后用gcc -S 编译它。 (或者你的编译器带有一些只会生成程序集的标志。)


我不确定它如何返回浮点/结构值。 可能它返回一个指向结构的指针 & 整个返回的结构在引擎盖下被 memcpy'ed,同时转换为 asm。有人可能会纠正:-)

【讨论】:

  • 您只是在描述许多可能的未定义行为之一。在这种情况下它并不真正相关,因为发布的代码不是有效的 C。
  • 这个答案与常见的编译器一致,如 gcc/visual studio 等。但是,在某些编译器上可能会有所不同。加上许多编译器,包括 gcc allowstandard C 的一些添加/放松。至少在 gcc 上,它只会给出警告,如果你省略返回类型 & 它会假设 @ 987654331@:warning: return type defaults to int [-Wreturn-type]
  • 但是您的答案不适用的许多其他 CPU 架构。对于每种这样的架构,都有各种不同的编译器,具有不同的调用约定。
  • 至于GCC,我认为给出警告是不正确的,我认为它应该给出错误。另请注意,仅当您使用 -std=c99 将代码编译为 C 时,GCC 才会发出此警告。如果将其编译为“非标准 GNU goo”,则不会收到警告,至少在 GCC 4.6.2 中不会。
  • @Lundin:如果您认为它应该给出错误,那么您应该使用-pedantic-errors 进行编译。警告足以符合标准,但我不知道您是否认为仅仅符合就足以避免标签“不正确”。这很不方便,从某种意义上说,那些主要使用默认选项并发布带有警告的代码的人可能没有意识到这些警告何时告诉他们他们的代码不是有效的 C99,因此即使使用-std=c99.
【解决方案3】:

自 C99 标准以来,您使用无效 C 代码发布的代码。

如果您使用的是实现 C90 标准的过时编译器,它将编译并且默认类型将是 int。我相信从这样的函数中省略 return 语句会导致未定义的行为,尽管我不能引用 C90 标准。

【讨论】:

  • “一个过时的编译器”——例如 gcc。或者 clang,带有合适的命令行选项。或 MSVC。这不是编译器实现什么的问题,而是代码是用什么语言编写的。如果它是用 C89 编写的,那么你可能需要一个 C89 编译器来构建它,即使它恰好也是有效的 C99,因为为谨慎起见,将其编译为 C89 通常比弄清楚它在 C99 中的含义是否与在 C89 中的含义相同更容易。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-11-01
  • 2021-09-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多