ゆとりーなの日記

日記的な事を書いて行くと思はれる

今日のポインタ

昨日までとは一転して普通のC言語の話が語られる予定です。お題はポインタです。なんで今更ポインタなんだということになるのが必然の流れですが、なんかC言語やってる人で物凄いことをやっている人を見てしまったので。

やっていたこと

//乱数を得る関数らしい
void getRandom(int val) {
  val = rand();
}

int main(void) {
  int rnd;
  getRandom(rnd);
  printf("%d", rnd); //何か変だ!?
  return 0;
}

getRandomを呼んでも、標準関数そのまま使えやという指摘は置いておいても、恐らく何も生産的なことは得られません。この関数を呼んでもrndは何も影響を受けないからです。
C言語では全ての引数は値渡しであるので、全てコピーされたものが使われます。上の関数に意図した働きをさせるには、次のように修正する必要があります。

void getRandom(int *val) {
  *val = rand();
}

int main(void) {
  int rnd;
  getRandom(&rnd);
  printf("%d", rnd); //何も変じゃない!?
  return 0;
}

こうすることで、関数にint型のポインタが渡されます。この時も渡されるのはあくまでポインタのコピーです。ポインタには*を前に付けると、そのアドレスにある変数にアクセスできるという機能があるので、元の変数が書き変えられるのです。勿論渡す値はアドレスである必要があるためrndの前に&を付ける必要があります。
しかしね。この手の関数は普通はこう書きますよね。

int getRandom(void) {
  return rand();
}

int main(void) {
  int rnd = getRandom();
  printf("%d", rnd); //何も変じゃない!?
  return 0;
}

まあintとポインタならたいていの環境であればサイズは同じでしょう。しかしcharやshortの場合はポインタの方がサイズがでかくなる可能性が高いですね。そうするとポインタ渡しの方が効率が悪いのです。よく言われるのが、組み込み型は値渡し、ユーザー定義型はポインタ渡しするのがいいとか言われますね。

そういえばこの問題を片づけている時に使われていたコンパイラ、確かGCCだったと思うんですけど、Cって値返す関数でreturn 値;書かなくてもコンパイル通るんですね。これで何度か引っかかりました。C++に移行したあとCに戻ると色々衝撃がでかいものです。