我已经使用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函数中的返回调用时。我明白这是不同的,我可以看到为什么这个代码没有任何内在错误,但它似乎很危险。我刚刚在我的一些非常古老的代码中发现了类似的错误,代表了一个可以追溯到很久的错误。我显然应该这样做:
i = change_i(10);
但是忘了,所以它从未改变过(我知道这个例子很愚蠢,确切的代码要复杂得多)。任何想法都会非常感激!
答案 0 :(得分:5)
它编译是因为调用函数并忽略返回结果是非常常见。事实上,主要的最后一行也是如此。
std::cout << "i = " << i << std::endl;
实际上是简称:
(std::cout).operator<<("i =").operator<<(i).operator<<(std::endl);
...并且您没有使用最终operator<<
返回的值。
一些静态检查器有选项可以在忽略函数返回时发出警告(然后选择注释通常忽略返回的函数)。 Gcc可以选择将函数标记为要求使用返回值(__attribute__((warn_unused_result))
) - 但只有在返回类型没有析构函数时它才有效: - (。
答案 1 :(得分:1)
忽略函数的返回值是完全有效的。以此为例:
printf("hello\n");
我们忽略了printf
的返回值,返回打印的字符数。在大多数情况下,您不关心打印多少个字符。如果编译器警告这一点,那么每个人的代码都会显示大量的警告。
这实际上是忽略表达式值的特定情况,在这种情况下,表达式的值是函数的返回值。
同样,如果你这样做:
i++;
你有一个表达式,其值被丢弃(即递增之前i
的值),但++
运算符仍然增加变量。
作业也是一种表达方式:
i = j = k;
这里有两个赋值表达式。一个是j = k
,其值是k
的值(刚刚分配给j
)。然后将此值用作右侧,另一个赋值为i
。然后丢弃i = (j = k)
表达式的值。
这与不从非void函数返回值非常不同。在这种情况下,函数返回的值是未定义的,尝试使用该值会产生undefined behavior。
没有任何关于忽略表达式值的未定义。
答案 2 :(得分:1)
允许它的简短原因是因为标准指定了这一点。
声明
change_i(10);
丢弃change_i()
返回的值。
更长的原因是大多数表达都有效果并产生结果。所以
i = change_i(10);
会将i
设置为8
,但分配表达式本身的结果也为8
。这就是为什么(如果j
类型为int
)
j = i = change_i(10);
会导致j
和i
的值均为8
。这种逻辑可以无限期地继续 - 这就是表达式可以被链接的原因,例如k = i = j = 10
。所以 - 从语言的角度来看 - 要求将函数返回的值赋给变量是没有意义的。
如果要显式放弃函数调用的结果,可以执行
(void)change_i(10);
和
之类的陈述j = (void)change_i(10);
将无法编译,通常是由于类型不匹配(int
无法分配void
类型的值。
所有这一切,如果调用者不使用函数返回的值,实际上可以配置几个编译器(和静态代码分析器)来发出警告。默认情况下会禁用此类警告 - 因此必须使用适当的设置进行编译(例如命令行选项)。
答案 3 :(得分:0)
我已经使用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 ++中的编译器。