views:

420

answers:

5

Hello! I have the following problem with a program which I wrote in Visual C++ and I hope that anyone can help me please:

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';
}
A: 

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.

Craig H
+3  A: 

You're copying the pointer when you pass 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.

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.

Kevin Montrose
+2  A: 

When your Spielfeld object is copied:

  • 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.
  • The copy has its own "Matrix" member, which is a copy of the original's "Matrix" member. But Matrix is a pointer, which means you have two pointers. 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.

Steve Jessop
+8  A: 

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. 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.

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. 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; 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. 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.

Brian Campbell
And ideally, if you're using C++, that would be done within a copy constructor.
Drew Hoskins
Yes, that is indeed correct, though he seems to be writing C code here so I don't know if he knows enough C++ to be able to apply that.
Brian Campbell
A: 

As interesting trivia: this is how call by value works in 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

Valentin