php 变量范围分析,内存分配和global,static关键字对变量的影响

变量分析

  • 局部变量:在函数(或者类的方法)内部,对应的变量为局部变量,仅应用于函数内部,生命周期:宏观的说,与函数生命周期基本一致,函数调用结束,局部变量也就被销毁了;
  • 全局变量:不在函数内部,一般指一个脚本中函数外部的变量,即全局变量,仅应用于当前页面,包括include或require的所有文件,生命周期:宏观的说,与脚本执行周期一致,脚本执行结束,全局变量也就被销毁了;
  • 超全局变量:php预定义的几个变量,其作用于在任何一个脚本的任何范围,即应用于整个网站 。常见的如下:$GLOBALS 、$_SERVER、$_REQUEST、$_POST 、 $_GET 、 $_FILES 、$_ENV 、 $_COOKIE、$_SESSION。
    具体实例可以参考文章C语言全局变量和局部变量讲解

但是这里要提到的是,php的全局变量有别于C/C++的全局变量的使用,即在函数外定义好全局变量后,如果在函数内部使用,需要使用global关键字或者$GLOBALS,具体代码如下:

$a=5;//定义全局变量
$b=6;
function test(){
	global $a,$b;   
    
    echo 'a='. ++$a;
    echo 'b='.$b;
		
}
test();
//printf a=6 b=6
$a=5;//定义全局变量
$b=6;
function test(){	 
    $c=$GLOBALS['a']+$GLOBALS['b'];
    echo $c;	
}
test()
//printf  11

注意 global关键字和$GLOBALS虽然效果一样,但其实现机制是不同的,根据php官方文档,描述如下:

在 Zend 引擎 1 代,它驱动了 PHP4,对于变量的 static 和 global 定义是以引用的方式实现的。例如,在一个函数域内部用 global 语句导入的一个真正的全局变量实际上是建立了一个到全局变量的引用。
1.$GLOBALS[‘var’]是外部的全局变量本身。
2.global $var是外部全局变量$var的同名引用。

内具体参考php文档全局和静态变量的引用

可以通过下面代码体会他们的不同:

$a=5;//定义全局变量
$b=6;
function test(){
	global $a,$b;   
    
	unset($a);
    echo 'a='.$a;
    echo 'b='.$b;
		
}
test();
echo 'a1='.$a;
//printf  Notice: (Undefined variable: a in 。。。) a= b=6 a1=5

可以看到unset之后,函数外部的a照常输出,仅函数内部的a被销毁。

$a=5;//定义全局变量
$b=6;
function test(){	 
    $GLOBALS['z']=$GLOBALS['a']+$GLOBALS['b'];
    unset($GLOBALS['a']);  
    echo 		
}
test();
echo 'z='.$z;
echo 'a1='.$a;
//printf  z=11 (Notice: Undefined variable: a  in ...  Notice: Undefined variable: a1 in ...)  a =   a1= 

可以看到unset之后,函数外部、内部的a都被销毁。

注: 本文的销毁并不表示变量所占的内存被释放,具体可以看看php的内存管理机制。

静态变量

静态变量就是通过static关键字修饰的变量,可以分为全局静态变量和局部静态变量,需要注意的是,静态变量的生命周期:全局静态变量与脚本执行周期一致,也就是与普通全局变量一致,而局部静态变量就与普通局部变量不同了,它是随着函数被调用开始而开始,脚本执行结束而结束。具体看以下代码:

static $a = 1;
function test1(){
	static $a=5;
	echo ++$a;
}

function test2(){
    global $a;
    echo ++$a;
}
function test3(){
   $a = 100;
    echo ++$a;
}
test1();  //6
test2();  //2
test1();  //7
test3();  //101
test2();  //3
test3();  //101

执行上述代码,可以有以下结论:

  1. 局部静态变量与全局静态变量不会冲突,当变量名字相同时。
  2. 静态变量仅在调用时初始化一次。

变量存储位置(内存分配)

就所有语言(C/C++/JAVA/PHP/Python)而言,程序运行时会被分配内存块,而内存在逻辑上大致区分为栈区、堆区、全局区(静态区)、代码区、常量区,但不同语言又会有所差别。php不同区的大致作用如下:

  1. 栈:存放变量名,对象名,(也会有一部分的整型,浮点型,布尔,数字型存放在此处),特点速度快,占内存空间小,栈中变量的内存会随着定义所在区间的结束自动释放,以局部变量$var=1为例,在函数开始调用时分配动态存储空间,函数结束时释放这些空间;
  2. 堆:c语言中由程序员调用malloc()函数来主动申请的,需使用free()函数来释放内存,若申请了堆区内存,之后忘记释放内存,很容易造成内存泄漏 。php中由new申请的内存块(即对象)放在堆中,~~由程序员释放(编译器不管),一般一个new与一个delete对应,一个new[]与一个delete[]对应。如果程序员没有释放掉,资源将由操作系统在程序结束后自动回收 。~~还有一些存储长度不一致, 占用内存较大的数据也放在堆里,特点速度慢,占的空间大。
  3. 全局区:存放静态变量,全局变量,其中的变量在程序运行期间会一直存在,不会释放;
  4. 代码区:存放要执行的函数和方法的opcode;
  5. 常量区:存放常量。常量区的规则只能写一次,所以其中的常量在程序运行期间会一直存在,不会释放,且常量在其中只有一份拷贝,不会出现相同的常量的不同拷贝。

区分变量与数据类型的关系。
运行该脚本

<?php
$var1='我是字符串,在堆里面';
static $var2=12;
$var3=1;
echo $var3;
?>

内存分配大致如下图所示:
在这里插入图片描述
如图,通过变量名与变量值的映射,完成变量的内存管理。

对象的管理
针对一下代码做测试,会发现对象有别于其他数据类型(数组,字符串,布尔等)。

对象:

class test{
	public $num;   
 }
 $s=new test;
$s->num=5;
echo $s->num;  //5

$b=$s;
$b->num=10;
echo $s->num;  //10
echo $b->num;  //10

$q=&$s;
$q->num=9;
echo $s->num; //9
echo $q->num; //9

$x=clone $s;
$x->num=100;
echo $s->num;  //5
echo $x->num;  //100

其他数据类型,以数组为例:

$b=array(array('5'),array('10'));
$c=&$b;
$c=array(array('50'),array('100'));
var_dump($b);  //array(array('50'),array('100'))
var_dump($c); //array(array('50'),array('100'))

$d=$b;
$d=array(array('50'),array('100'));
var_dump($b); //array(array('5'),array('10'))
var_dump($d); //array(array('50'),array('100'))

可以看到,php对象的管理有别于其他数据类型,对象引用和间接赋值效果一样,而其他数据类型则不同,具体可以看看php变量的引用赋值与传值赋值的详细介绍

参考博文

  1. java与php 的static区别
  2. php中static 静态关键字在类中的用法
  3. C/C++中static关键字的用法
  4. c语言中static关键字用法详解
  5. 通过案例讲解PHP static 关键字作用
  6. C语言中局部变量与全局变量在内存中的存放位置
  7. 全局变量和局部变量在内存中的区别
  8. 什么是代码区、常量区、静态区(全局区)、堆区、栈区?
  9. PHP,常量/变量与内存间的关系
  10. php对象的内存分配机制
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章