当前位置: 首页 > news >正文

PHP的

文章目录

  • 环境
  • 问题
  • 分析
  • 解决方法
  • 考一考
  • 总结
  • 何时使用&
    • 循环里修改元素值
    • 函数内修改参数值
    • 多个变量指向同一个内存地址
    • 函数返回引用

环境

  • PHP 8.4.5
  • Windows 11 专业版

问题

看下面的PHP代码:

$arr1 = [1, 2, 3];foreach($arr1 as &$v) { // 这里加上&,是为了修改原数组的值$v++;
}// 其它代码......foreach($arr1 as $v) {echo $v;
}

代码分析:

  1. 初始化数组 arr1
  2. 遍历数组,给每个元素加1。这里 &$v 中的 & 表示引用,也就是说, $v 指向的是实际的数组元素,这是因为需要修改数组元素的值,如果不加 & ,则是复制了一份(对 $v 的修改不影响数组)
  3. 遍历数组,打印每个元素的值

看起来简单直观,期望的输出结果是 234

然而,实际输出结果却是 233

分析

和很多其它语言一样,PHP在循环结束后,并不会自动清除循环变量。

比如:在Java语言里:

        for (; i <= 10; i++) {......}System.out.println("i = " + i);

在循环结束后,允许访问变量 i ,此时其值为 11

Delphi (Object Pascal)语言也类似:

    for i := 1 to 10 dobegin......end;writeln('i = ', i);

在循环结束后,允许访问变量 i ,此时其值为 11 ,不过编译器会给出一个警告。

回到PHP,在上面的代码里,第一个循环里用的是 &$v ,在循环结束后,数组变成了 2, 3, 4 。注意,此时 $v 指向数组最后一个元素(其值为 4 )。

在第二个循环里,每次迭代时会给 $v 赋值,但是别忘了, $v 指向的是数组最后一个元素,所以,给 $v 赋值,就会覆盖数组的最后一个元素。

  • 在第一次迭代时,数组变成了 2, 3, 2 (把第一个元素值赋给 $v ,覆盖最后一个元素)
  • 在第二次迭代时,数组变成了 2, 3, 3 (把第二个元素值赋给 $v ,覆盖最后一个元素)
  • 在第三次迭代时,数组仍然是 2, 3, 3 (把最后一个元素值赋给 $v ,覆盖最后一个元素)

解决方法

有两种解决方法:

  • 在第一次循环结束后,使用 unset($v) 来清理变量,也就是说,第二个循环里的 $v 是一个全新的变量
  • 在第二次循环里换个变量名

最好是两种方法都用。

当然,如果第一个循环里没用 & ,那么第二个循环里继续使用 $v 也没问题。

考一考

下面的代码,输出结果是什么?

$arr1 = [1, 2, 3];foreach($arr1 as &$v) { // 这里加上&,是为了修改原数组的值$v++;
}foreach($arr1 as &$v) {echo $v;
}

答案:输出结果是:

234

这里的输出结果,反而是期望的正确结果。

这是因为,在第二个循环里也用了引用 & ,所以每次迭代时, $v 会正确的指向每个元素。

总结

加不加 & ,本质的区别在于:

  • 不加 & 时:复制。在第一个例子中,会把迭代的元素值“复制”到 $v (注意 $v 指向了数组最后一个元素)
  • 加上 & 时:不复制,而是把 $v 改为指向迭代的元素。

换句话说:

  • 不加 & 时, $v 的内存地址不会变化,并把迭代的元素值复制过来:
    • 如果 $v 变量已经存在,则继续用它(第一个例子就是这种情况)
    • 如果 $v 变量不存在,则创建一个全新变量,后续迭代时,其内存地址不再变化
  • 加上 & 时, $v 不管是不是全新的变量,都会随着每次迭代,将其内存地址动态指向迭代的元素 ( $v 没有开辟另外的内存地址)。

何时使用&

原则:能不用 & 就不用,因为它增加了复杂度,可能会引起潜在的不易发现的问题。

比如一个函数的定义为 function func1($arr1) ,那我就可以放心的传一个数组进去,而不用担心该函数会修改我的数组。

& 常见的使用场景如下:

循环里修改元素值

foreach($arr1 as &$v) {$v++;
}unset($v);

函数内修改参数值

function swap(&$a, &$b) {$tmp = $a;$a = $b;$b = $tmp;
}

注:如果没记错,Pascal里,带不带 & ,分别叫做“值参”和“变参”。

function func1(&$arr1) { // 如果不带 & ,整个数组会被复制一份......$arr1[0]++;
}

注:PHP 5+ 对写时复制(Copy-On-Write)有优化,也就是说,即使是传值,也只有在确实有修改时,才会复制。因此,多数情况下,无需因为考虑性能而使用传引用的方式。

多个变量指向同一个内存地址

$a = 1;
$b = &$a; // $b 和 $a 指向同一个值
$b = 2;
echo $a; // 输出 2($a 也被修改)

函数返回引用

function &getSingleton() {static $instance = null;if ($instance === null) {$instance = new HeavyObject();}return $instance; // 返回引用,避免复制
}
$obj = &getSingleton(); // 必须用 & 接收

在链式API中,也经常需要返回原来的对象,而不能复制一个新对象。

相关文章:

  • 做投票网站有没有免费的crm系统软件
  • 网站栏目结构设计电脑培训速成班多少钱
  • thinkcmf做网站快不快北京网站定制公司
  • asp动态网站开发论文搜索引擎优化排名
  • 西宁企业做网站短视频推广
  • 淘宝客做网站可行么北京网聘咨询有限公司
  • DeepSeek16-open-webui Pipelines开发填坑
  • 课堂笔记:吴恩达的AI课(AI FOR EVERYONE)-W1 机器学习什么能做,什么不能做
  • 算法 按位运算
  • 缓存与加速技术实践-MongoDB数据库应用
  • 阿里云ACP-检索分析服务
  • 深入解析Python多服务器监控告警系统:从原理到生产部署
  • 解锁阿里云Datatransport:数据迁移的终极利器
  • 向量数据库milvus中文全文检索取不到数据的处理办法
  • ISP Pipeline(5): Auto White Balance Gain Control (AWB) 自动白平衡
  • 城市综合管廊监测,智能化安全监测,多源感知,三维可视化监控
  • ASIO 避坑指南:高效、安全与稳健的异步网络编程
  • 基于SpringBoot的智慧旅游系统
  • 六个安全Agent设计模式:有效防止Prompt注入攻击
  • Serverless新宠:阿里云SAE,解锁应用部署新姿势
  • 【攻防篇】解决:阿里云docker 容器中自动启动xmrig挖矿
  • dockercompose快速安装ELK
  • Elasticsearch索引字段的类型
  • 伏羲微官网企业建站授权证书/防伪查询/三合一应用【前端开源】
  • Java项目:基于SSM框架实现的健康管理系统【ssm+B/S架构+源码+数据库】
  • 什么是Redis?