Neunomizuの日記

俺だけが俺だけじゃない

あなたがものを渡すときに考慮しなければならない3つの渡し方

tags: 情報

どうも.コンピュータサイエンスを極める前にRuby on Railsを極めないとインターン先でやばい,ピカチュウです

今回はプログラミングにおける渡し方を整理したいと思います

題名は中身がない記事にありそうな雰囲気を意識しました.想像通り中身はないのでご安心ください

プログラミングにおける渡し方

プログラミングにおける変数の渡し方は以下の3つがあります

  • 値渡し
  • ポインタ(アドレス)渡し
  • 参照渡し

(渡し方なんてどうでもいいと思った人は,好きな人にプレゼントを着払いで渡せばいいと思います)

渡し方というのはコンピュータのメモリを理解すればすんなり理解できるので,サンプルコードと表をセットで説明したいと思います

その前に...メモリとは?

コンピュータには主記憶装置,通称メモリ(memory)というものがあります

このメモリにはそれぞれアドレスというのが割り振られています

そして,そのアドレス上で値を保持するのがプログラミングにおける変数の役割です

そのメモリをどう扱うかが渡し方によって異なるわけです

値渡し

サンプルコードはC言語です

int x = 1; # xのアドレスを確保し,そのアドレスの指す値に1を入れている
int y = x; # yのアドレスを確保し,そのアドレスの指す値にxのアドレスが指す値,1を入れている
変数名 アドレス
x m1 1
y m2 1

値渡しは上のように変数(y)のあるアドレスに指す値に,他の変数(x)の値を代入する際に渡すことです

ここではアドレスのやり取りはなく,値のやり取りのみが行われます

ポインタ(アドレス)渡し

C言語において,ポインタとして宣言されるとアドレス,値どちらの値も示すことができます

一方,単に整数として宣言された場合はそのアドレスの指す値を示します

ポインタの宣言,ポインタ以外アドレスを指す方法,アドレスの指す値を指す方法です

int* x; // 宣言
&x; // アドレス
*y; // 値

サンプルコードはC言語です

int x = 1; // xのアドレスを確保し,そのアドレスの値に1を入れている
int* y; // ポインタ型であるyのアドレスを確保している
y = &x; // yの値にxのアドレスを入れている
変数名 アドレス
x m1 1
y m2 m1

参照渡し

サンプルコードはRubyです

x = 1
y = x
変数名 アドレス
x m1 1
y m1 1

参照渡しはアドレスをコピーして渡すやり方です

この渡し方でバグを生んだことしかないのです

次にその一例を載せておきます

Rubyでソート関数を書いたときの話

このなんたら渡しというのは別に変数に変数を代入するときだけでなく,関数に代入するときも関係します(仮引数に変数を代入していると考えるといいと思います)

Rubyの場合,配列,ハッシュを引数とする関数へは参照渡しが行われます

以下のようなバブルソートを用意します

引数にa(配列)とn(配列の要素数)を受け取ります

def bubble_sort(arr, n)
  flag = true
  while flag
    flag = false
    (1..n - 1).reverse_each do |i|
      next unless arr[i] < arr[i - 1]

      arr[i], arr[i - 1] = arr[i - 1], arr[i]
      flag = true
    end
  end
  arr
end

この関数でソートしてみると以下のような結果になります

arr = [*(1..10)].reverse!
# => [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
sorted_arr = bubble_sort(arr)
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
puts sorted_arr
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

そうです,引数の配列もひっくり返ってしまっています

このように意図しない副作用が起きてしまうので注意が必要です

ちなみに

sorted_arr = bubble_sort(arr.dup)

のように引数に複製した配列を与えると配列を意図しない形でソートせずに返すことが可能です

まとめ

プレゼントを渡す時は渡し方にも拘ろう