我们不必将函数的返回值分配给变量吗?C/C++
Don't we have to assign return values of the functions to variables? C/C++
我已经使用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
的结果。这就是为什么(如果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
类型的值)。
说了这么多,实际上可以配置一些编译器(和静态代码分析器),以便在调用者没有使用函数返回的值时发出警告。这些警告在默认情况下是关闭的-因此有必要使用适当的设置(例如命令行选项)进行编译。
我已经使用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++中的编译器无法使用函数定义
- 如何创建一个CMake变量,除非显式重写,否则使用默认值
- 将成员变量添加到共享库中的类中,不会破坏二进制兼容性吗
- 将数组的地址分配给变量并删除
- 为"adjacent"变量赋值时出现问题
- enum是C++中的宏变量还是整数变量
- 在全局变量中保存类的实例以重新创建类(创建"backup")
- 用C++中的一个变量定义一个常量
- 具有奇怪重复模板模式的派生类中的成员变量已损坏
- 你能重载对象变量名本身返回的内容吗
- 内置函数可查看CPP中的成员变量
- 是否可以初始化不可复制类型的成员变量(或基类)
- 尝试通过多个向量访问变量时,向量下标超出范围
- 试图让变量检查数组中的某些内容
- Cpp-Tuple使用带有变量的get
- 将包含C样式数组的对象初始化为成员变量(C++)
- 当vector是tje全局变量时,c++中vector的内存管理
- 通过多个头文件使用常量变量
- std::threads可以从Windows DLL中的全局变量创建/销毁吗?
- 执行函数时导致崩溃的变量
- 变量没有改变?通过向量的函数调用