从技术上讲,所有函数都可以是无效函数吗?

时间:2016-04-28 13:33:42

标签: c function

例如:

int f1() {
    return 3;
}
void f2(int *num) {
    *num = 3;
}
int n1, n2;
n1 = f1();
f2(&n2);

使用f1,我们可以返回一个值并执行" variable = f1()" 但是同样可以使用void函数来完成,该函数在给定地址的情况下更新该变量的值,而不必执行" variable = f1()"。

那么,这是否意味着我们实际上只能将void函数用于一切?或者是否有一些void函数无法替换另一个int函数/(类型)函数?

4 个答案:

答案 0 :(得分:7)

将所有内容都设置为无效功能(在某些人的词典中被称为"例程")的主要问题是您无法轻松链接它们:

f(g(x))

成为,如果你真的想链接它:

int gout;
f((g(x, &gout), gout))

这很痛苦。

答案 1 :(得分:3)

是的,您可以为所有内容使用void返回类型,并完全依赖于通过修改后的参数返回。实际上,您可以完全避免使用函数并将所有内容放在main方法中。

与该语言的任何其他功能一样,返回值会为您提供特别的优势,由您决定是否需要它们。以下是返回值的一些优点:

  • 可以将返回值分配给const变量,这可以使您的代码更容易推理
  • 编译器可以为返回值应用某些类型的优化(这更适用于C ++ RVO,但也可能适用于C的结构;我不确定)
  • 使用返回值的代码通常更容易阅读,尤其是当函数是数学函数时(例如,如果需要输出,想象必须使用sin / cos /等手动声明所有临时对象进行大数学运算)通过参数)。比较:

    double x = A*sin(a) + B*cos(b);
    

    double tmpA, tmpB;
    sin(&tmpA, a);
    cos(&tmpB, b);
    double x = A * tmpA + B * tmpB;
    

    或使用John Zwinck在答案中建议的类似结构:

    double tmpA, tmpB;
    double x = A * (sin(&tmpA, a), tmpA) + B * (cos(&tmpB, b), tmpB);
    
  • 无论函数内部发生什么,都可以保证设置该值,因为这是由编译器强制执行的(除了一些非常特殊的情况,例如longjumps)

  • 您无需担心检查是否使用了指定的值;你可以返回值,如果请求者不需要它,他们可以忽略它(将其与你需要的替代方法中的所有需要​​NULL检查相比较)

当然也有缺点:

  • 您只获得一个返回值,因此如果您的函数在逻辑上返回多种类型的数据(并且它们无法在逻辑上组合成单个结构),则通过参数返回可能会更好
  • 由于需要复制它们,大型对象可能会引入性能损失(这就是为什么RVO是在C ++中引入的原因,这使得这不是一个问题)

答案 2 :(得分:2)

  

那么,这是否意味着我们实际上只能将void函数用于一切?

事实上。事实证明,这样做是一种相当常见的编码风格。但是这些样式通常表明应该始终为错误代码保留返回值,而不是void

在实践中,你通常无法始终如一地坚持这种风格。有一些特殊情况,不使用返回值变得不方便。

例如,在编写标准C泛型函数bsearchqsort使用的回调函数时。期待回调格式

int compare (const void *p1, const void *p2);

其中函数返回小于零,大于零或零。在设计方面,保持参数以只读方式传递非常重要,您不希望通用搜索算法突然开始修改搜索到的内容。因此,虽然理论上没有理由为什么这些函数也不能成为void返回类型,但在实践中它会使代码更难以阅读。

答案 3 :(得分:1)

当然可以;但这并不是一个好主意。

它可能并不总是方便或导致易于理解的代码。返回void的函数不能直接用作表达式中的操作数。例如,你可以写:

if( f1() == 3 )
{
    ...
}

f2()你必须写:

f2( &answer ) ;
if( answer )
{
    ...
}

另一个问题是访问控制之一 - 通过将指针传递给您正在为该函数提供间接访问调用者数据的函数,只要函数表现良好且不会溢出,这就很好。指针可以引用单个对象或对象数组 - 该指针必须执行适当规则的函数,因此本质上不太安全。