C: Передача структуры в функцию не приводит к операции вызова по значению - PullRequest
2 голосов
/ 08 января 2010

У меня есть следующая проблема с программой, которую я написал на 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';
}

Ответы [ 5 ]

9 голосов
/ 08 января 2010

Когда вы передаете структуру, вы передаете ее по значению. Однако матрица внутри него реализована как указатель на указатель на int. Эти указатели являются ссылками, и поэтому, когда вы изменяете значение, на которое они ссылаются в вашей функции, на то же значение ссылается исходная структура в main.

Если вы хотите передать эти объекты по значению, вам нужно сделать глубокое копирование самостоятельно, в котором вы выделите новую матрицу и скопируете в нее все значения из исходной матрицы.

Как указывает Дрю, в C ++ предпочтительным способом реализации этой глубокой копии является конструктор копирования . Конструктор копирования позволяет выполнять глубокое копирование в любое время, когда объект передается по значению, без необходимости явно копировать объект самостоятельно.

Если вы еще не готовы к классам и конструкторам, вы можете просто написать функцию, возможно Spielfeld copySpielfeld(Spielfeld original), которая выполнит эту глубокую копию; по сути он будет таким же, как ваш код инициализации, который вы использовали в своем примере, за исключением того, что он будет принимать значения из переданного Spielfeld вместо создания нового Spielfeld. Вы можете вызвать это перед передачей field в функцию show или сделать так, чтобы функция show сделала это для любого переданного аргумента, в зависимости от того, как вы хотите, чтобы ваш API работал.

3 голосов
/ 08 января 2010

Вы копируете указатель при передаче fieldToShow. Передача по значению не выполняет глубокое копирование, поэтому оба значения Spielfeld при вызове show(...) и main(...) (хотя и различаются) имеют одинаковое значение для матрицы.

Исправить это нетривиально. Вероятно, проще всего было бы изменить show(...) на передачу по ссылке (в основном используя Spielfeld*) и сделать явную копию в начале функции.

2 голосов
/ 08 января 2010

Когда копируется ваш объект Spielfeld:

  • Копия имеет свой собственный «ходок», который является копией оригинала «ходок». Поскольку Уокер - это структура, это означает, что у вас есть две структуры.
  • Копия имеет собственный элемент Matrix, который является копией члена Matrix оригинала. Но Matrix - это указатель, что означает, что у вас есть два указателя. Копия указателя указывает на то же, на что указывает оригинал.

Таким образом, изменения содержимого ходунка копии не влияют на оригинал, потому что у него разные ходунки. Изменения в содержании матрицы копии влияют на оригинал, потому что они используют одну и ту же матрицу.

1 голос
/ 08 января 2010

Структура начинает передаваться по значению, но так как она содержит указатель (матрицу), на что указывает этот указатель, может быть изменен любым, кто имеет доступ к структуре. Если вы не хотите, чтобы это происходило, вы можете сделать указатель постоянным.

0 голосов
/ 08 января 2010

Как интересные мелочи: так работает вызов по значению в Java. Объект ссылки всегда передаются по значению. Если вы манипулируете объектами, на которые жестко указывают эти ссылки, это будет означать, что вызов по ссылке произошел.

В действительности не имеет ничего общего с вашим вопросом, но, возможно, вы находите это интересным.

Счастливого взлома

...