我们不必将函数的返回值分配给变量吗?C/C++

Don't we have to assign return values of the functions to variables? C/C++

本文关键字:变量 C++ 分配 不必 函数 返回值 我们      更新时间:2023-10-16

我已经使用C/c++大约三年了,我不敢相信我以前从来没有遇到过这个问题!

下面的代码编译(我刚刚尝试使用gcc):

#include <iostream>
int change_i(int i) {
  int j = 8;
  return j;
}
int main() {
  int i = 10;
  change_i(10);
  std::cout << "i = " << i << std::endl;
}

程序输出i = 10,如您所料。

我的问题是——为什么这个可以编译?我预计会出现一个错误,或者至少是一个警告,说明返回了一个未使用的值。

我天真地认为这与意外忘记非void函数中的返回调用类似。我明白这是不同的,我可以看到为什么这段代码本身没有什么问题,但它似乎很危险。我刚刚在我的一些非常旧的代码中发现了一个类似的错误,这代表了一个可以追溯到很久以前的bug。我显然想做:
i = change_i(10);

但是忘记了,所以它从来没有改变过(我知道这个例子很愚蠢,确切的代码要复杂得多)。任何想法都将非常感激!

之所以编译,是因为调用函数并忽略返回结果非常常见。事实上,main的最后一行也是如此。

    std::cout << "i = " << i << std::endl;

实际上是

的缩写
    (std::cout).operator<<("i =").operator<<(i).operator<<(std::endl);

…并且您没有使用从最终operator<<返回的值。

一些静态检查器有选项在函数返回值被忽略时发出警告(然后有选项对返回值经常被忽略的函数进行注释)。Gcc有一个将函数标记为需要使用返回值的选项(__attribute__((warn_unused_result)))——但它只在返回类型没有析构函数的情况下才有效:-(。

忽略函数的返回值是完全有效的。例如:

printf("hellon");

我们在这里忽略了printf的返回值,它返回打印的字符数。在大多数情况下,您不关心打印了多少字符。如果编译器对此发出警告,每个人的代码都会显示大量的警告。

这实际上是忽略表达式值的一种特殊情况,在这种情况下,表达式的值是函数的返回值。

同样,如果你这样做:

i++;

您有一个表达式的值被丢弃(即i的值在被加1之前),但是++操作符仍然对变量加1。

赋值也是表达式:

i = j = k;

这里有两个赋值表达式。一个是j = k,它的值是k的值(刚刚赋值给j)。然后将该值用作对i的另一个赋值的右侧。然后丢弃i = (j = k)表达式的值。

这与非void函数不返回值非常不同。在这种情况下,函数返回的值是未定义的,尝试使用该值会导致未定义的行为。

忽略表达式的值并没有什么未定义的。

允许的简短原因是,这是标准所规定的。

声明

 change_i(10);

丢弃change_i()返回的值。

更长的原因是大多数表达式既具有效果又产生结果。所以

i = change_i(10);

i设置为8,但赋值表达式本身也有8的结果。这就是为什么(如果jint类型)

j = i = change_i(10);

将使ji的值都为8。这种逻辑可以无限地继续下去——这就是表达式可以被链接的原因,比如k = i = j = 10。因此,从语言的角度来看,要求将函数返回的值赋给变量是没有意义的。

如果您想显式地放弃函数调用的结果,可以执行

(void)change_i(10);

和像

这样的语句
j = (void)change_i(10);

无法编译,通常是由于类型不匹配(int不能赋值给void类型的值)。

说了这么多,实际上可以配置一些编译器(和静态代码分析器),以便在调用者没有使用函数返回的值时发出警告。这些警告在默认情况下是关闭的-因此有必要使用适当的设置(例如命令行选项)进行编译。

我已经使用C/c++大约三年了

我可以假设在这三年中你使用了标准的C函数printf。例如

#include <stdio.h>
int main( void )
{
    printf( "Hello World!n" );
}

函数的返回类型与void不同。但是,我确信在大多数情况下,您没有使用函数的返回值:)

如果要求编译器在不使用函数的返回值时发出错误,那么类似于上面所示的代码将无法编译,因为编译器无法访问函数的源代码,也无法确定函数是否有副作用。:)

考虑另一个标准C函数——字符串函数。

例如,函数strcpy声明为

char * strcpy( char *destination, const char *source );

例如,如果您有以下字符数组

char source[] = "Hello World!";
char destination[sizeof( source )];

则函数通常以

方式调用
strcpy( destination, source );
当你只需要复制一个字符串时,使用它的返回值是没有意义的。此外,对于所示示例,您甚至可以不写
destination = strcpy( destination, source );

编译器将发出一个错误。

因此,正如你所看到的,忽略函数的返回值是有意义的。

对于您自己的示例,编译器可能会发出一个消息,该函数没有副作用,因此它的调用是过时的。在任何情况下,它都应该发出一条消息,表明函数参数未被使用。:)

考虑到有时编译器看不到存在于其他编译单元或库中的函数定义。因此编译器无法确定函数是否有副作用

在大多数情况下,编译器处理函数声明。有时C和c++中的编译器无法使用函数定义