プログラミングをしていて勝手に値が変わってしまうということは、多くの人が経験しているでしょう。ほとんどは製作者のミス(意図しない挙動)であると思いますが、中にはプログラミング言語の仕様で勝手に変わってしまうものもあります。今回はその代表例である配列や参照渡しについて説明します。
勝手に変わる例
サンプルコード
以下コードを適当な関数に記載し、実行します。
内容は長さ3のstring配列(A、B)を用意し、AをBへ代入後、要素を編集・出力をしています。
//配列を宣言 string[] array_A = new string[]{"A0", "A1", "A2" }; string[] array_B = new string[3]; //配列Bに配列Aを代入 array_B=array_A; //表示① Console.WriteLine("編集前---"); Console.WriteLine(" ①array_A --> {0},{1},{2}", array_A[0], array_A[1], array_A[2]); Console.WriteLine(" ①array_B --> {0},{1},{2}", array_B[0], array_B[1], array_B[2]); //両方の配列の要素を書き換えてみる array_A[0] = "a0"; array_B[1] = "b1"; //表示② Console.WriteLine("編集後---"); Console.WriteLine(" ②array_A --> {0},{1},{2}", array_A[0], array_A[1], array_A[2]); Console.WriteLine(" ②array_B --> {0},{1},{2}", array_B[0], array_B[1], array_B[2]);
実行結果は以下画像のようになります。
array_A[0]に「a0」を代入すると、array_B[0]も同じ値に、またarray_B[1]に「b1」を代入するとarray_A[1]も同じ値になっていることが分かります。
何か知らないところで繋がっており、同一人物?のような振る舞いをしていますね。
なぜ変わってしまうのか
結論から言うと、配列は「参照渡し」と呼ばれる少し特殊な代入(値を渡す)方法が行われるからです。
(対義語は「値渡し」で、いわゆる普通の代入のイメージ)
参照渡しとは、要素の中身(上の例では文字”A0″,”A1″など)を渡すのではなく、その要素が保存されている記憶領域情報を代入します。
つまりコード上で「=(イコール)」を用いて代入しても、等しくなるのは値ではなく保存場所なのです(結果的に値も等しくなりますが)。
「A=B」とした後の状況のイメージ図は以下の通りです。
上のサンプルプログラムでは、配列A・Bは記憶領域情報を共有しているため、一方の要素を編集すると、もう一方の要素も変化したように見えたわけです。
「参照渡し」の対義語は「値渡し」で、いわゆる数学の代入と同じです。
変わらないようにするための解決策
参照渡しの要素を代入して、そのあと編集すると代入元も変化してしまいます。
それを防ぐ方法は、コピー先に複製したものを代入する、です。
以下に配列(参照渡し)の例を示します。
//配列を宣言 string[] array_A = new string[]{"A0", "A1", "A2" }; string[] array_B = new string[3]; //配列Bに配列Aを代入 Array.Copy(array_A, array_B, array_A.Length); //表示① Console.WriteLine("編集前---"); Console.WriteLine(" ①array_A --> {0},{1},{2}", array_A[0], array_A[1], array_A[2]); Console.WriteLine(" ①array_B --> {0},{1},{2}", array_B[0], array_B[1], array_B[2]); //両方の配列の要素を書き換えてみる array_A[0] = "a0"; array_B[1] = "b1"; //表示② Console.WriteLine("編集後---"); Console.WriteLine(" ②array_A --> {0},{1},{2}", array_A[0], array_A[1], array_A[2]); Console.WriteLine(" ②array_B --> {0},{1},{2}", array_B[0], array_B[1], array_B[2]);
実行結果は以下のようになります。
配列A、Bの各要素をそれぞれ編集できました。
まとめ
予期せぬ値の変化があった場合は、扱っている型が「参照渡し」かどうかを調べてみましょう。
そしてもしそうであれば、その型の複製方法が必ずあるのでそれを活用しましょう。
私はプログラミングを始めたばかりのころ、参照渡しのことを知らずに自身のプログラムのバグかと思い、長いこと考えた思い出があります。
参照渡しなんて数学にないので、想像できるわけがなかった。。。
コメント