c-从技术上讲,所有函数都可以是一个空函数吗



例如:

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函数/(type)函数的功能?

将所有东西都设为void函数(在一些人的词典中被称为"例程")的主要问题是你不能轻易地将它们链接起来:

f(g(x))

成为,如果你真的想连锁它:

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

这很痛苦。

是的,您可以对所有内容使用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;
    

    或者使用约翰·兹温克在他的回答中建议的类似结构:

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

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

当然也有缺点:

  • 你只得到一个返回值,所以如果你的函数在逻辑上返回多种类型的数据(并且它们不能在逻辑上组合成一个结构),那么通过参数返回可能会更好
  • 由于需要复制大型对象,它们可能会带来性能损失(这就是在C++中引入RVO的原因,这使得问题大大减少)

那么,这是否意味着我们实际上可以对所有事情都使用void函数?

的确如此。事实证明,这样做是一种相当常见的编码风格。但是,与void不同,这种样式通常规定返回值应始终保留给错误代码。

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

例如,当编写标准C泛型函数bsearchqsort所使用的类型的回调函数时。预期格式的回调

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

其中函数返回小于零、大于零或零。从设计角度来看,保持传递的参数为只读是很重要的,你不希望你的通用搜索算法突然开始修改搜索内容。因此,虽然理论上没有理由说这类函数也不能是void返回类型,但在实践中,这会使代码变得更丑陋、更难阅读。

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

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

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

对于f2(),您必须写入:

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

另一个问题是访问控制——通过传递指向函数的指针,您可以让该函数间接访问调用方的数据,只要该函数行为良好且没有溢出,这是可以的。指针可以指单个对象或对象数组——使用该指针的函数必须强加适当的规则,因此本质上不太安全。

最新更新