ホームページ  >  記事  >  バックエンド開発  >  PHP の foreach コピーはいつ行われますか?

PHP の foreach コピーはいつ行われますか?

藏色散人
藏色散人オリジナル
2019-01-28 11:53:362582ブラウズ

この記事では、PHP 5 の foreach のコピー動作について説明します。 PHP の内部動作、つまり zvals、refcount、コピーオンライトの動作についての知識が必要です。

PHP の foreach コピーはいつ行われますか?

#PHP の foreach は、非常にきちんとした、要点を絞った言語構造です。それでも、遅いと考えて使いたくない人もいます。一般的な名前の理由の 1 つは、foreach が反復する配列をコピーするためです。

したがって、より直観的かつ直接的に書くのではなく、

$keys = array_keys($array);
$size = count($array);
for ($i = 0; $i < $size; $i++) {
    $key   = $keys[$i];
    $value = $array[$key];

    // ...
}

と書くことを提案する人もいます:

foreach ($array as $key => $value) {
    // ...
}

ここには 2 つの問題があります:

マイクロ最適化は悪いです。多くの場合、それは時間を無駄にするだけで、目に見えるパフォーマンスの向上にはつながりません。

foreach のコピー動作は、ほとんどの人が考えているよりも少し複雑です。通常、「最適化された」バージョンは元のバージョンよりも遅くなります。

foreach はいつコピーされますか?

foreach が配列をコピーするかどうか、およびコピー数は 3 つのことに依存します:

反復配列が参照されるかどうか、それ refcount の高さ、および反復が参照によって行われるかどうか。

No Reference, refcount == 1

以下のコードでは、$array は参照されず、refcount は 1 です。この場合、foreach は配列をコピーしません (証明)。これは、foreach が参照なしで反復された配列を常にコピーするという一般的な考えに反してです。

test();
function test() {
    $array = range(0, 100000);
    foreach ($array as $key => $value) {
        // ...
    }
}

理由は簡単です: なぜこれを行うのですか? foreach が $array を変更する唯一の場所は、それが内部配列ポインターであるためです。これは予期された動作であるため、予防する必要はありません。

未参照、refcount > 1

以下のコードは、前のコードとよく似ています。唯一の違いは、配列がパラメーターとして渡されることです。これは重要ではないように思えるかもしれませんが、foreach の動作が変わります。

これで、値ではなく配列構造がコピーされます (証拠。これが単にコピーされた構造であると疑問に思う場合は、比較してください)このスクリプトとそのスクリプト。最初のスクリプトは構造のみをコピーし、2 番目のスクリプトは両方をコピーします)。

$array = range(0, 100000);
test($array);
function test($array) {
    foreach ($array as $key => $value) {
        // ...
    }
}

これは一見すると少し奇妙に思えるかもしれません:

配列がパラメータとして渡された場合はコピーされるのに、関数で定義されている場合はコピーされないのはなぜですか? その理由は次のとおりです。配列 zval は複数の変数間で共有されるようになりました。関数の外側の $array 変数と関数内の $array 変数です。 foreach が配列構造をコピーせずに配列を反復すると、関数内の $array 変数の配列ポインタだけでなく、関数外の $array 変数のポインタも変更されます。したがって、foreach は配列構造 (つまり、ハッシュ テーブル) をコピーする必要があります。一方、値は引き続き zval を共有できるため、コピーする必要はありません。

引用

次の状況は、前の状況と非常によく似ています。唯一の違いは、配列が参照によって渡されることです。この場合、配列はコピーされません (証明)。

$array = range(0, 100000);
test($array);
function test(&$array) {
    foreach ($array as $key => $value) {
        // ...
    }
}

この場合、同じ理由が前のケースに当てはまります。つまり、外側の $array と内側の $array が zval を共有します。違いは、それらが参照 (isref == 1) であることです。そのため、この場合、内側の配列に対する変更は外側の配列にも適用されます。したがって、内側の配列の配列ポインタが変更されると、外側の配列の配列ポインタも変更される必要があります。 foreach がコピーを必要としないのはこのためです。

参照による反復

#上記の例はすべて、値によって反復されます。参照の反復の場合も同じルールが適用されますが、値の参照を追加すると、配列値のコピーの動作が変更されます (構造のコピーに関する動作は同じままです)。

「未参照、refcount == 1」の場合は変更されていません。参照反復とは、$ 値に変更があった場合に、配列がコピーされないように元の配列を変更することを意味します (証明)。

「参照される」場合も同じままです。この場合、$value を変更すると、反復配列を参照するすべての変数が変更されます (証明)。

「未参照、refcount > 1」の場合のみ変更されています。これは、配列構造とその値をコピーする必要があるためです。そうしないと、関数の外側の $array 変数の配列ポインタが変更され、$value を変更すると、外側の $array 値も変更されてしまうためです (証明)。

概要

反復配列が参照されておらず、refcount > 1 である場合に限り、foreach は配列構造をコピーします。

foreach はまた、前の点が適用され、反復が参照によって行われる場合に限り、配列値をコピーします

以上がPHP の foreach コピーはいつ行われますか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。