搜索
首页后端开发php教程谨慎使用PHP的引用类型详解_PHP教程
谨慎使用PHP的引用类型详解_PHP教程Jul 13, 2016 pm 05:00 PM
phpreference使用引用类型详解

谨慎使用PHP的引用 引用类型(Reference)在许多计算机语言中都被使用,而且是作为一个非常强大而实用的特性存在。它有类似指针(Pointer)的实现,却又有不同于指针的表现。

例如C++的引用,可以让不同变量指向同一个对象,同时又保有直接使用dot来获取对象成员,不用繁琐的使用dereference运算符(*)和Pointer to Member运算符(->)。Java和C#中就直接以引用为主要类型,尽量让开发人员避免使用指针。

PHP中也引入了引用类型,在对对象赋值传递上,基本可视为是同于Java/C#的引用传递(具体请见Objects and references)。但同时又支持在基础类型上通过引用运算符(&)来获得内容的引用。不过在实际的使用中,PHP的引用类型因为整个PHP设计结构而存在着许多的问题,使得在程序出现非预计的结果。

 引用变量可被赋予新的引用
在C++中,引用类型的变量只能在其定义时被赋予引用值,所以我们只要追踪到变量的定义处就可以知道变量是在操作哪个内容。

但是PHP不同,PHP里模糊了变量的定义,可以不定义就使用的变量。所以可以让变量被多次赋予引用值。

 

 代码如下 复制代码

$x = 21;
$y = 7;

$z = &$x;
$z = &$y;

var_dump($x,$y,$z);初次看起来,让人的感觉是$z变成了对$x的引用,然后让$z的内容变成了对$y的引用,也就是说$x和$z都成对$y的引用。但是实际输出结果是:

 代码如下 复制代码

int(21)
int(7)
int(7)从结果上看出,$x保持不变,只是$z被改变成了对$y的引用。相当于先unset了$z变量然后赋予了新值。

$z = &$x;
unset($z);
$z = &$y;这其实是比较合理逻辑,就比如下边的代码,我们并不是得到类似于“指向指针的指针(Pointer point to a Pointer)”那样的“引用引用的引用(Reference refer to a Referenece)”,只是多个引用到同一块内容的引用变量。

$x = 21;
$y = &$x;
$z = &$y

引用数组元素会让该元素变成引用类型
对于变量上取引用,并不会造成原变量类型的改变,但是如果取的是数组中的元素,却会让该元素也变成引用类型。

在看问题代码前,首先要指出的是:

 Array assignment always involves value copying. Use the reference operator to copy an array by reference.

也就是说PHP的数组赋值是copy而非引用,赋值过程会创建新的数组赋予被赋值的变量。在新变量上的数组操作并不会影响到原数组变量中的内容。

 代码如下 复制代码


$a = array(21, 7);
$b = $a;
$b[0] = 7;
var_dump($a);
echo '
';
var_dump($b);

//Output:
//array(2) { [0]=> int(21) [1]=> int(7) }
//array(2) { [0]=> int(7) [1]=> int(7) }下边我们再来看看如果引用数组中的元素,会有什么异常。


$a = array(21, 7);
$c = & $a[0];
$b = $a;
$b[0]= "21";
$b[1]= "7";

var_dump($a);
echo '
';
var_dump($b);
echo '
';
var_dump($c);
echo '
';

// Output:
// array(2) { [0]=> &string(2) "21" [1]=> int(7) }
// array(2) { [0]=> &string(2) "21" [1]=> string(1) "7" }
// string(2) "21"

代码中$b跟之前的只是简单的赋值,只是在之前多了一部取第一个元素的引用,但理应还是拷贝了一个新的数组。可是结果却是对$b的修改,同时也改变了$a的第一个元素,而第二个元素没有影响。

从输出中我们还看到了一个不寻常的地方,就是数组第一个元素的类型多一个‘&’符号。而这个正是取引用运算符。也就是说数组的第一个元素已经变成了引用类型。所以赋值时也是引用拷贝,而非值拷贝。

这个问题十分奇怪,在开发中也造成了许多不必要的困扰,原本以为拷贝出来的数组并没有跟原数组有关联,但是就因为这意外出现的引用类型,让我在操作时也影响到了原数组。

我也不清楚这算是PHP中的bug,还是有意如此设计。在网上找了很久也没有对该方便的相关解释,只有Float Middle的《PHP: References To Array Elements Are Risky》和 Symmetric Designs的《Problems w/accessing a PHP array by reference》里有谈到这个,但是也没有讲原因。

之后又在PHP的Bug Report中看到几篇有联系的报告(Bug6417, Bug7412, Bug15025, Bug20993)。有些说这是个Bug,而且已经在后边的版本被修复。具体我也没有明白,只能避免在数组上使用引用。

 更有趣的事情是,如果unset那些引用,只留下一个,那么数组元素又会变成不含有引用的正常类型。

 代码如下 复制代码

unset($b);
unset($c);
var_dump($a);
   
// Output:
//array(2) { [0]=> string(2) "21" [1]=> int(7) }

避免使用PHP的引用
这个其实这是PHP Array Manual里面提到的要注意的地方,最常发生在foreach的之中,希望通过引用来改变远数组的值(可参见该篇文章)。

其实想通过使用foreach配合引用来改变数组元素的值,主要是因为PHP的数组是Associative Array,这种数组“不定长度,索引可以不连续,可同时用字符串和整数当索引”,所以我们无法用for循环简单增加整数索引。

当然我们可以像下边的代码那样通过$key直接对数组元素改变值,但是这可能存在一定的效率问题。

 代码如下 复制代码
foreach ($array_var as $key => $value)
    $array_var [$key] = $newValue;

另一个常用的引用的地方是在函数调用中使用引用传递参数。其主要原因是希望通过这种方法让函数实现返回多个返回值。比如我们希望用一个表示指示函数是否在执行中出现error而导致返回值是无效的。

但是因为PHP的函数是可以返回不同的类型的,所以并不需要传入引用参数来作为表示。即使真的需要多个返回值,也可以通过返回“以字符串为主键的数组”作为解决方案,只不过可能需要在文档中指出每个元素都是对应那个结果。

 有一个比较好操作方式,应该是每当引用变量不再需要使用时,就即时对该变量使用unset让它切换与内容之间的联系。而且即使该变量不是引用类型,我们确认它不再被使用,对它调用unset也不会有什么问题。至少保证在之后对该变量重新赋值时,并不会影响到之前的结果。

www.bkjia.comtruehttp://www.bkjia.com/PHPjc/631276.htmlTechArticle谨慎使用PHP的引用 引用类型(Reference)在许多计算机语言中都被使用,而且是作为一个非常强大而实用的特性存在。它有类似指针(Pointer)的实...
声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
如何在Go中使用命名管道?如何在Go中使用命名管道?May 11, 2023 pm 04:22 PM

命名管道是一种在操作系统中相对比较低级的进程通信方式,它是一种以文件为中介的进程通信方式。在Go语言中,通过os包提供了对命名管道的支持。在本文中,我们将介绍如何在Go中使用命名管道来实现进程间通信。一、命名管道的概念命名管道是一种特殊的文件,可以被多个进程同时访问。在Linux系统中,命名管道是一种特殊的文件类型,它们存在于文件系统的某个位置上,并且可以在

如何在Go中使用第三方库?如何在Go中使用第三方库?May 11, 2023 pm 03:30 PM

在Go语言中,使用第三方库是非常方便的。许多优秀的第三方库和框架可以帮助我们快速地开发应用程序,同时也减少了我们自己编写代码的工作量。但是如何正确地使用第三方库,确保其稳定性和可靠性,是我们必须了解的一个问题。本文将从以下几个方面介绍如何使用第三方库,并结合具体例子进行讲解。一、第三方库的获取Go语言中获取第三方库有以下两种方式:1.使用goget命令首先

如何在PHP中使用协程?如何在PHP中使用协程?May 12, 2023 am 08:10 AM

随着传统的多线程模型在高并发场景下的性能瓶颈,协程成为了PHP编程领域的热门话题。协程是一种轻量级的线程,能够在单线程中实现多任务的并发执行。在PHP的语言生态中,协程得到了广泛的应用,比如Swoole、Workerman等框架就提供了对协程的支持。那么,如何在PHP中使用协程呢?本文将介绍一些基本的使用方法以及常见的注意事项,帮助读者了解协程的运作原理,以

如何在PHP中使用变量函数如何在PHP中使用变量函数May 18, 2023 pm 03:52 PM

变量函数是指可以使用变量来调用函数的一种特殊语法。在PHP中,变量函数是非常有用的,因为它可以让我们更加灵活地使用函数。在本文中,我们将介绍如何在PHP中使用变量函数。定义变量函数在PHP中,变量函数的定义方式非常简单,只需要将要调用的函数名赋值给一个变量即可。例如,下面的代码定义了一个变量函数:$func='var_dump';这里将var_dump函

如何在Go中使用WebSocket?如何在Go中使用WebSocket?May 11, 2023 pm 04:17 PM

近年来,WebSocket技术已经成为了Web开发中不可或缺的一部分。WebSocket是一种在单个TCP连接上进行全双工通信的协议,它使得客户端和服务器之间的通信更加流畅和高效。如今,很多现代的Web应用程序都使用了WebSocket技术,例如实时聊天、在线游戏以及实时数据可视化等。Go语言作为一个现代的编程语言,自然也提供了很好的支持WebSock

如何在 Windows 11 中按需使用 OneDrive 的文件如何在 Windows 11 中按需使用 OneDrive 的文件Apr 14, 2023 pm 12:34 PM

<p>Windows 系统上的 OneDrive 应用程序允许您将文件存储在高达 5 GB 的云上。OneDrive 应用程序中还有另一个功能,它允许用户选择一个选项,是将文件保留在系统空间上还是在线提供,而不占用您的系统存储空间。此功能称为按需文件。在这篇文章中,我们进一步探索了此功能,并解释了有关如何在 Windows 11 电脑上的 OneDrive 中按需使用文件的各种选项。</p><h2>如何使用 On

如何在Go中使用音频处理?如何在Go中使用音频处理?May 11, 2023 pm 04:37 PM

随着音频处理在各种应用场景中的普及,越来越多的程序员开始使用Go编写音频处理程序。Go语言作为一种现代化的编程语言,具有优秀的并发性和高效率的特点,使用它进行音频处理十分方便。本文将介绍如何在Go中使用音频处理技术,包括读取、写入、处理和分析音频数据等方面的内容。一、读取音频数据在Go中读取音频数据有多种方式。其中比较常用的是使用第三方库进行读取,比如go-

如何在PHP中使用数据聚合函数如何在PHP中使用数据聚合函数May 18, 2023 pm 02:51 PM

数据聚合函数是一种用于处理数据库表中多行数据的函数。在PHP中使用数据聚合函数可以使得我们方便地进行数据分析和处理,例如求和、平均数、最大值、最小值等。下面将介绍如何在PHP中使用数据聚合函数。一、介绍常用的数据聚合函数COUNT():计算某一列的行数。SUM():计算某一列的总和。AVG():计算某一列的平均值。MAX():取出某一列的最大值。MIN():

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
2 周前By尊渡假赌尊渡假赌尊渡假赌
仓库:如何复兴队友
1 个月前By尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island冒险:如何获得巨型种子
4 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )专业的PHP集成开发工具

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

SublimeText3 英文版

SublimeText3 英文版

推荐:为Win版本,支持代码提示!

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)