从返回类型的函数中获取结果'void'而结果变量是输入参数之一 - C++

Getting results from functions with 'void' return type, while resulting variable is one of the input arguments - C++

本文关键字:结果 参数 输入 C++ 变量 函数 获取 返回类型 void      更新时间:2023-10-16

我得到了这个数学例程库(没有文档)来完成大学的一些任务。问题是它的所有函数都有void返回类型,尽管这些函数相互调用,或者是另一个函数的一部分,并且需要它们的计算结果。

这是从库中提取的一段(简化的)代码。不要为代码中的数学而烦恼,它并不重要。只是传递参数和返回结果让我感到困惑(如代码后面所述):

// first function
void vector_math // get the (output) vector we need
 ( 
   double inputV[3], // input vector
   double outputV[3] // output vector
 )
 {
      // some variable declarations and simple arithmetics
      // .....
      //
      transposeM(matrix1, matrix2, 3, 3 ); // matrix2 is the result
      matrixXvector( matrix2, inputV, outputV) // here you get the result, outputV
 }
////////
// second function
 void transposeM // transposes a matrix 
    ( 
      std::vector< std::vector<double> > mat1, // input matrix
      std::vector< std::vector<double> > &mat2, // transposed matrix
      int mat1rows, int mat1columns 
    )
   {         
   int row,col;
    mat2.resize(mat1columns);  // rows
     for (std::vector< std::vector<double> >::iterator it=mat2.begin(); it !=mat2.end();++it)
          it->resize(mat1rows);
 for (row = 0; row < mat1rows; row++)
     {
     for (col = 0; col < mat1columns; col++)
         mat2[col][row] = mat1[row][col];
     }
   }
   ////////
   // third function
   void matrixXvector // multiply matrix and vector
    (
          std::vector< std::vector<double> > inMatrix,
          double inVect[3],
          double outVect[3]
    )
      {
     int row,col,ktr;
     for (row = 0; row <= 2; row++)
      {
     outVect[row]= 0.0;
     for (ktr = 0; ktr <= 2; ktr++)
         outVect[row]= outVect[row] + inMatrix[row][ktr] * inVect[ktr];
       }
   }

所以"vector_math"被主程序调用。它以inputV作为输入,结果应该是outputV。但是,outputV是输入参数之一,该函数返回void。之后在调用"转置"answers"matrixXvector"时会发生类似的过程。

为什么输出变量是输入参数之一?如何返回结果并将其用于进一步的计算?这种传递和返回参数是如何工作的?

因为我是一个初学者,也从来没有见过这种风格的编码,我不明白如何传递参数,特别是在这些函数中给出输出。因此,我不知道如何使用它们,也不知道对它们有什么期望(它们实际会做什么)。因此,如果您能解释清楚这些过程,我将非常感激。

EXTRA:

谢谢大家的精彩回答。这是我第一次几乎无法决定接受哪个答案,即使我这样做了,我也觉得对别人不公平。我想添加一个额外的问题,如果有人愿意回答(作为评论就足够了)。

这是一种返回某些或多个值的"旧"(但仍然流行)风格。它是这样工作的:

void copy (const std::vector<double>& input, std::vector<double>& output) {
    output = input;
}
int main () {
    std::vector<double> old_vector {1,2,3,4,5}, new_vector;
    copy (old_vector, new_vector); // new_vector now copy of old_vector
}

所以基本上你给函数一个或多个输出参数来写它的计算结果。

如果你通过值传递输入参数(即你不打算改变它们)或通过const引用传递它们并不重要,尽管通过值传递只读参数可能会在性能方面付出代价。在第一种情况下,您复制输入对象并在函数中使用副本,在后一种情况下,您只是让函数看到原始对象并防止它被const修改。输入参数的const是可选的,但是省略它允许函数更改它们的值,这可能不是您想要的,并且禁止将临时值作为输入传递。

输入形参必须通过非const引用传递,以允许函数更改它/它们。

另一种更老的"C-isher"风格是传递输出指针或原始数组,就像第一个函数一样。这是潜在的危险,因为指针可能没有指向有效的内存块,但仍然是相当广泛的分布。它的工作原理与第一个示例基本相同:

// Copies in to int pointed to by out
void copy (int in, int* out) {
    *out = in;
}
// Copies int pointed to by in to int pointed to by out
void copy (const int* in, int* out) {
    *out = *in;
}
// Copies length ints beginning from in to length ints beginning at out
void copy (const int* in, int* out, std::size_t length) {
    // For loop for beginner, use std::copy IRL:
    // std::copy(in, in + length, out);
    for (std::size_t i = 0; i < length; ++i)
        out[i] = in[i];
}

Baum的回答是准确的,但可能没有C/c++初学者想要的那么详细。

进入函数的实际参数值总是按值传递(即位模式),不能以调用者可读的方式更改。然而——这是关键——实参中的那些位实际上可能是指针(或引用),它们不直接包含数据,而是包含内存中包含实际值的位置。

示例:在这样的函数中:

void foo(double x, double output) { output = x ^ 2; }

将输出变量命名为"output "不会改变任何东西——调用者没有办法得到结果。

但是像这样:

void foo(double x, double& output) { output = x ^ 2; }

"&"表示输出参数是对应该存储输出的内存位置的引用。它是c++中的语法糖,相当于下面的"C"代码:

void foo(double x, double* pointer_to_output) { *pointer_to_output = x ^ 2; }

指针解引用被引用语法所隐藏,但思想是一样的。

数组执行类似的语法技巧,它们实际上是作为指针传递的,因此

void foo(double x[3], double output[3]) { ... }

void foo(double* x, double* output) { ... }

本质上是等价的。注意,在这两种情况下都无法确定数组的大小。因此,通常认为传递指针和长度是一种好做法:

void foo(double* x, int xlen, double* output, int olen);

像这样的输出参数在多种情况下使用。常见的一种方法是返回多个值,因为函数的返回类型只能是单个值。(虽然可以返回包含多个成员的对象,但不能直接返回多个单独的值。)使用输出参数的另一个原因是速度。如果所讨论的对象很大且/或构造成本很高,那么就地修改输出通常会更快。

另一种编程范例是返回一个指示函数成功/失败的值,并在输出参数中返回计算值。例如,很多历史上的Windows API都是这样工作的。

数组是一种底层c++结构。可隐式转换为指向为该数组分配的内存的指针。

int a[] = {1, 2, 3, 4, 5};
int *p = a; // a can be converted to a pointer
assert(a[0] == *a);
assert(a[1] == *(a + 1));
assert(a[1] == p[1]);
// etc.

关于数组的令人困惑的事情是函数声明void foo(int bar[]);等同于void foo(int *bar);。所以foo(a)不会复制数组a;相反,a被转换为指针,然后复制指针(而不是内存)。

void foo(int bar[]) // could be rewritten as foo(int *bar)
{
    bar[0] = 1; // could be rewritten as *(bar + 0) = 1;
}
int main()
{
    int a[] = {0};
    foo(a);
    assert(a[0] == 1);
}

bar指向与a相同的内存,因此修改bar指向的数组内容与修改数组a 的内容相同。

在c++中你也可以通过引用传递对象(Type &ref;)。您可以将引用视为给定对象的别名。所以如果你写:

int a = 0;
int &b = a;
b = 1;
assert(a == 1);

b实际上是a的别名-通过修改b可以修改a,反之亦然。函数也可以通过引用接受实参:

void foo(int &bar)
{
    bar = 1;
}
int main()
{
    int a = 0;
    foo(a);
    assert(a == 1);
}

再说一遍,bar只不过是a的别名,所以通过修改bar,你也可以修改a


您拥有的数学例程库正在使用这些特性将结果存储在输入变量中。这样做是为了避免复制和简化内存管理。正如@Baum mit Augen所提到的,该方法还可以用作返回多个值的方法。

考虑以下代码:

vector<int> foo(const vector<int> &bar)
{
    vector<int> result;
    // calculate the result
    return result;
}

当返回result时,foo将复制vector对象,根据存储元素的数量(和大小),复制的代价可能非常大。

注意:大多数编译器会使用命名返回值优化(NRVO)来省略上面代码中的副本。但在一般情况下,你不能保证它会发生。

另一种避免昂贵复制的方法是在堆上创建结果对象,并返回指向已分配内存的指针:
vector<int> *foo(const vector<int> &bar)
{
    vector<int> *result = new vector<int>;
    // calculate the result
    return result;
}

调用者需要管理返回对象的生命周期,在不再需要delete时调用它。如果不这样做,可能会导致内存泄漏(内存保持分配,但实际上不可用,由应用程序)。

注意:有各种解决方案可以帮助返回(复制成本很高)对象。c++ 03有std::auto_ptr包装器来帮助对堆上创建的对象进行生命周期管理。c++ 11在语言中增加了移动语义,允许通过值

而不是使用指针有效地返回对象