2016-08-12 88 views
6

我一直在使用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,正如您所料。

我的问题是 - 为什么编译?我会预料到一个错误,或者至少是一个警告,说有一个返回值没有被使用。

天真,我会认为这是一个类似的情况下,当你不小心忘记了在非空函数的返回调用。我知道这是不同的,我可以看到为什么这个代码没有什么固有的错误,但它看起来很危险。我刚在我的一些非常古老的代码中发现了一个类似的错误,代表了一个长期存在的错误。我明明意思做:

i = change_i(10); 

却忘了,所以这是从来没有改变(我知道这个例子是愚蠢的,确切的代码要复杂得多)。任何想法将不胜感激!

+4

[g ++如何在忽略函数返回值时得到警告](http://stackoverflow.com/questions/2870529/g-how-to-get-warning-on-ignoring-function-return-value ) – Starl1ght

+5

鉴于赋值('=')是一个表达式,对每个未使用的表达式发出警告将会产生问题。 – EOF

+1

请参见C++中的[nodiscard属性](http://stackoverflow.com/documentation/c%2b%2b/5251/attributes/19006/nodiscard#t=201608121315059275439)17 – Jarod42

回答

1

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

printf("hello\n"); 

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

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

同样,如果这样做:

i++; 

您有其值被丢弃的表达式(即i递增之前的值),但是操作者++仍然递增变量。

的分配也是一个表达:

i = j = k; 

在这里,你有两个赋值表达式。一种是j = k,其值为k(刚分配给j)。然后将此值用作右侧另一个分配给i。然后丢弃i = (j = k)表达式的值。

这是不从非void函数返回一个值非常不同。在这种情况下,函数返回的值是未定义的,尝试使用该值会导致undefined behavior

没有什么不确定约忽略一个表达式的值。

+0

为了明确它,你也可以使用'(void)discarded'。 – edmz

5

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

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

实际上是短期的:

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

...你不使用从最终operator<<返回的值。

某些静态检查器可以选择在忽略函数返回时发出警告(然后选择注释通常忽略返回值的函数)。海湾合作委员会有一个选项来标记一个功能,要求使用返回值(__attribute__((warn_unused_result))) - 但它只适用于如果返回类型没有析构函数:-(。

1

它允许的一个短的原因是因为这是什么标准规定。

声明

change_i(10); 

丢弃由change_i()返回的值。

较长的原因是,大多数的表达均具有影响和产生的结果。所以,

i = change_i(10); 

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

j = i = change_i(10); 

会导致两个ji拥有的8值。这种逻辑可以无限期地继续 - 这就是表达式可以链接的原因,例如k = i = j = 10。因此 - 从语言的角度来看 - 要求函数返回的值被分配给一个变量是没有意义的。

如果你想明确地丢弃函数调用的结果,这是可以做到

(void)change_i(10); 

和像

声明
j = (void)change_i(10); 

将不能编译,通常是由于不匹配类型(int不能指定void类型的值)。如果调用者不使用函数返回的值,实际上可以配置几个编译器(和静态代码分析器)以发出警告。这种警告默认是关闭的 - 所以需要用适当的设置进行编译(例如命令行选项)。

0

我一直使用C/C++约3年

我可以假设,在这三年中,你使用标准的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++编译器。