简体   繁体   English

C:将结构传递给函数不会导致按值运算调用

[英]C: Passing Struct to a Function doesn't result in a call by value operation

I have the following problem with a program which I wrote in Visual C++ and I hope that anyone can help me please: 我在Visual C ++中编写的程序存在以下问题,希望有人能帮助我:

typedef struct spielfeld
{
 int ** Matrix;
 int height; 
 int width; 
 Walker walker;
 Verlauf history;
} Spielfeld;

void show(Spielfeld fieldToShow); //Prototype of the Function where I have this
                                  //problem

int main(int argc, char *argv[])
{
  int eingabe;
  Spielfeld field;

  //Initialize .. and so on

  //Call show-Function and pass the structure with Call by Value
  show(field);
  //But what's happened? field.Matrix has changed!!
  //can anyone tell me why? I don't want it to become changed!
  //cause that's the reason why I pass the field as Call by Value!
}

void show(Spielfeld fieldToShow)
{
 //Here is the problem: Alltough the parameter fieldToShow has been passed
 //with call by value, "fieldToShow.Matrix[0][0] = 1" changes the field in 
 //main!!
 fieldToShow.Matrix[0][0] = 1;

 //Another try: fieldToShow.walker.letter only affects the local fieldToShow, 
 //not that field in main! That's strange for me! Please help!
 fieldToShow.walker.letter  = 'v';
}

When you pass the structure in, you are passing it in by value. 当您传入结构时,您将按值传递它。 However, the matrix within it is implemented as a pointer to pointer to int. 但是,其中的矩阵实现为指向int的指针。 Those pointers are references, and so when you modify the value referenced by them in your function, the same value is referenced by the original structure in main . 这些指针是引用,因此当您在函数中修改它们引用的值时, main的原始结构将引用相同的值。

If you want to pass these objects by value, you need to do a deep copy yourself, in which you allocate a new matrix, and copy all of the values from the original matrix into it. 如果要按值传递这些对象,则需要自己进行深层复制,在其中分配新矩阵,并将原始矩阵中的所有值复制到其中。

As Drew points out, in C++, the preferred way to implement that deep copy is via a copy constructor . 正如Drew所指出的,在C ++中,实现深度复制的首选方法是通过复制构造函数 A copy constructor allows you to perform your deep copy any time your object is passed by value, without having to explicitly copy the object yourself. 复制构造函数允许您在按值传递对象时执行深层复制,而无需自己显式复制对象。

If you are not ready for classes and constructors yet, you can simply write a function, perhaps Spielfeld copySpielfeld(Spielfeld original) , that will perform that deep copy; 如果你还没有为类和构造函数做好准备,你可以简单地编写一个函数,也就是Spielfeld copySpielfeld(Spielfeld original) ,它将执行那个深层复制; it will essentially be the same as your initialization code that you elided in your example, except it will take values from the Spielfeld passed in, instead of creating a new Spielfeld . 它基本上与您在示例中省略的初始化代码相同,除了它将从传入的Spielfeld获取值,而不是创建新的Spielfeld You may call this before passing your field into the show function, or have the show function do it for any argument passed in, depending on how you want your API to work. 您可以在将field传递给show函数之前调用它,或者让show函数为传入的任何参数执行此操作,具体取决于您希望API如何工作。

You're copying the pointer when you pass fieldToShow . 您在传递fieldToShow时正在复制指针。 Pass-by-value does not perform a deep copy, so both the Spielfeld in an invocation of show(...) and main(...) (although distinct) have the same value for Matrix. 按值传递不执行深层复制,因此在调用show(...)main(...) (尽管不同)时Spielfeld具有相同的Matrix

Fixing this is non-trivial. 解决这个问题并非易事。 Probably the easiest thing to do would be to change show(...) to pass-by-reference (using a Spielfeld* basically) and make an explicit copy at the start of the function. 可能最简单的方法是将show(...)更改为pass-by-reference(基本上使用Spielfeld* )并在函数的开头创建一个显式副本。

When your Spielfeld object is copied: 复制Spielfeld对象时:

  • The copy has its own "walker", which is a copy of the original's "walker". 该副本有自己的“助行器”,这是原版“助行器”的副本。 Since walker is a struct, that means you have two structs. 由于walker是一个结构,这意味着你有两个结构。
  • The copy has its own "Matrix" member, which is a copy of the original's "Matrix" member. 该副本具有自己的“Matrix”成员,该成员是原始“Matrix”成员的副本。 But Matrix is a pointer, which means you have two pointers. 但Matrix是一个指针,这意味着你有两个指针。 A copy of a pointer points to the same thing the original points to. 指针的副本指向原始指向的相同内容。

So, modifications to the contents of the copy's walker don't affect the original, because they have different walkers. 因此,对副本助行器内容的修改不会影响原件,因为它们具有不同的助行器。 Modifications to the contents of the copy's matrix do affect the original, because they share the same matrix. 对副本矩阵内容的修改会影响原始内容,因为它们共享相同的矩阵。

The structure is begin passed by value, but since it contains a pointer (the matrix) what that pointer is pointing to can be changed by anyone that has access to the structure. 结构以值开始传递,但由于它包含指针(矩阵),因此指针所指向的内容可由有权访问该结构的任何人更改。 If you don't want this to happen, you can make the pointer const. 如果您不希望发生这种情况,可以使指针成为常量。

As interesting trivia: this is how call by value works in java. 作为有趣的琐事:这是按值调用java的方式。 Object references are always passed by value. 对象引用始终按值传递。 If you manipulate the objects to which these references point tough it will feel like call by reference happened. 如果你操纵这些引用所指向的对象很难,那么就会觉得通过引用调用了。

Has really nothing to do with your question but maybe you find that interestring. 真的与你的问题无关,但也许你发现了这个问题。

Happy hacking 快乐的黑客

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM