特集・コラム

メニュー

PHPer上級者になるために知っておきたいこと

vol.39 ‐ PHP 『関数仕様編 その4(参照について)』

投稿日時:2014/06/12 01:06

カテゴリ: PHPer上級者になるために知っておきたいこと

さて。参照について、です。
「引数による参照渡し」を理解するために、まず「PHPにおける参照の代入」について、
少しコードを伴わせつつ見ていきましょう。
まずは普通のコードから。

$a = 10;
$b = $a;
$b ++;
var_dump($a);
var_dump($b);

これを、少しだけ、変えてみます。

$a = 10;
$b = &$a; // XXX ここを変えた
$b ++;
var_dump($a);
var_dump($b);

$bの値を変えたはずなのに$aも変化してしまいます。
ごくおおざっぱに、これが「リファレンス渡し」の理解のベースになる、「参照による代入」となります。

で…この話をすると、他言語…特にC言語とかC++とかをやっている人達のイメージ的には「ようはポインタだろ?」とか思う訳なのですが、結論からいうと「言語の、変数管理のレイヤーレベルで、全然違います」ってのが
答えになります。
その辺りを意識せずに「他言語でこうだったからPHPでもうまくいくべさ」とばかりに参照を縦横無尽に使うと、
割と悲劇が待っておりますので。
本稿では、その悲劇を、全力で食い止めるべく、努力をしてみたいと思います。

顕著なのがC言語なので、少しだけC言語のお話を交ぜていきますが。
イメージ的に、似たようなコードを書いて、その違いについて説明をしていきたいと思います。
(ちなみに、ここで書いている「PHPのコード」と「C言語のコード」は、実は”全然違うこと”をやっていますが、
“同じだと勘違いされやすいコード”って事で書いてます)

$a = 10;
$b = &$a;
$b ++;
echo $a , “\n”;

#include
int main() {
int a = 10;
int *b = &a;
(*b)++;
printf(“%d\n”, a);
//
return 0;
}

変数名などを出来るだけ一致させて、比較しやすいように書いてみました。
どちらも「変数aに10を代入」「変数bに、変数aの参照(リファレンス/ポインタ)を代入」
「変数bの値をインクリメント」「変数aを表示」てな事をやっている点において、一緒です。
ただ、上述の中身は「全く違う処理」をやっているために、これを混ぜると大変に危険でございます。

まず、分かりやすい(…と思う)、C言語のほうから見ていきましょう。
C言語において、変数は宣言された時点で、どこか(スタック領域)にメモリ空間が確保されます。例えばおいちゃんの現在の環境ですと、変数aは、0xbfafec60という番地のメモリにおかれていました(実行タイミングによって微妙にアドレス番地が変わりますので、参考程度に)。
なので、変数bの中にも、同じ値が入っています。…ちなみに「変数b自体」は別のメモリ空間(0xbfafec5c)に確保されておりますが、まぁそれは余談。
この辺は、以下のコードを動かして、確認してみてください。

#include
int main() {
int a = 10;
int *b = &a;
(*b)++;
printf(“%d\n”, a);
printf(“%x\n”, &a); // 変数aがあるメモリ番地の表示
printf(“%x\n”, b); // 変数bの値の表示
printf(“%x\n”, &b); // こっちは余談なんで、あってもなくても
//
return 0;
}

結果
11
bff9fad0
bff9fad0
bff9facc

この辺りの「ポインタの概念」は、その辺になれていない言語圏の人的には少し難しいところ、とはよく聞きますが。
ある程度以上いくと「必要になってくる」ので、理解しておくとよいと思われます。
で…この感覚のまんまPHPを扱うと、物凄く酷い目にあいます。

PHPでは、「$a = 10;」のタイミングで、そこそこ複雑な処理を、裏側でやっています。
先に、C言語でそれっぽく書いてみましょう。

$a = 10;

というPHPコードを、C言語でそれっぽく書いてみます。

#include
int main() {
int zval_1 = 10;
int *a = &zval_1;
//
return 0;
}

色々まだ語弊がありますが、大体こんな感じでしょうか。
さて…C言語だけで説明していくと「お前なんの言語のコラム書いてるんだ!」と怒られそうなので、
少し、日本語で書いていきましょう。

PHPは、変数を「2つのテーブル」で管理します。
一つが「変数名を管理するテーブル」で、もう一つが「実際の値を管理するテーブル」です。
この「2つのテーブル」で、「$a = 10;」がどのように扱われているかを、箇条書きにして書いてみましょう。

・とりあえず「整数値10」”を”代入するので、「実際の値を管理するテーブル」に「整数, 10」ってのを追加。この行は「1行目」に挿入された
・「変数a」”に”代入するので、「変数名を管理するテーブル」に「変数名はaで、値は「実際の値を管理するテーブルの1行目」にある」って情報を挿入

変数名を管理するテーブル

変数名

値が入っている所の行数

a

1行目

実際の値を管理するテーブル

行数

変数の型

1行目

整数

10

こんな風に動きます。

さて。ここから「$b = &$a;」と「$b ++;」を見ていきましょう。

$b = &$a;
・とりあえず「$aの参照」を代入するので。$aの実際の値は「実際の値を管理するテーブルの1行目」にあるので、そこを覚えておく
・「変数b」”に”代入するので、「変数名を管理するテーブル」に「変数名はbで、値は「実際の値を管理するテーブルの1行目」にある」って情報を挿入

変数名を管理するテーブル

変数名

値が入っている所の行数

a

1行目

b

1行目

実際の値を管理するテーブル

行数

変数の型

1行目

整数

10

$b ++;
・変数bの値があるのは、「変数名を管理するテーブル」によると「実際の値を管理するテーブルの1行目」
・「実際の値を管理するテーブルの1行目」の値をインクリメント

変数名を管理するテーブル

変数名

値が入っている所の行数

a

1行目

b

1行目

実際の値を管理するテーブル

行数

変数の型

1行目

整数

11

って演算が入ります。
なので、結果的に「$aも$bも、値はインクリメントされている」訳ですね。

せっかくなので「参照による代入」と「普通の代入」の双方を書いておきます。
厳密には「普通の代入」の動きはもうちょいとややこしいのですが(copy-on-writeってのがあってですね…)、
一端、少し分かりやすい「嘘」を書きます。
つっても、次稿あたりでその辺も正しますが。
各テーブルは、コードが実行された後の状態です。

$a = 10;
$b = &$a;
$c = $a;
$a ++;
$b ++;
$c ++;

変数名を管理するテーブル

変数名

値が入っている所の行数

a

1行目

b

1行目

c

2行目

実際の値を管理するテーブル

行数

変数の型

1行目

整数

12

2行目

整数

11

テーブルの値になる理由を、しっかりと「ソースコードの実行順番に、空っぽのテーブルに自分で埋めていく」形で
追いかけて頂きつつ。
特濃なこのネタは、次回に引っ張って行きたいと思います。…数回程度で落ち着くと思いますので。
PHPにおける「参照」という概念について、今しばらくおつきあいいただければ幸いです。

バックナンバーはこちら

IT系のお仕事特集

お仕事のご紹介には、まずヒューマンリソシアへの登録が必要です。
ヒューマンリソシア人材派遣サイトの便利な機能 ・お気に入りの派遣求人のブックマーク ・登録会への予約 ・有給休暇の管理 ・WEB給与明細の確認 ・お気に入りの情報をメール受信
登録会の入力手続きをあらかじめおこなえます。

ヒューマンリソシア派遣サービスに 登録する 無料

ページトップへ戻る
ヒューマンリソシア派遣サービスに 登録する 無料
ページトップへ戻る