php如何移动数组内部指针
天蓬老师
天蓬老师 2017-04-10 14:22:41
[PHP讨论组]

php的数组都有一个内部指针,指向数组的元素,初始化的时候是第一个,我要便利数组,让内部指针逐个移动

$arr = array ('a', 'b', 'c', 'd', 'e');
foreach ($arr as $k => $v) {
    $curr = current($arr);
    echo "{$k} => {$v} -- {$curr}\n";
}

得到结果是

0 => a -- b
1 => b -- b
2 => c -- b
3 => d -- b
4 => e -- b

内部指针向后移动了一位就再也没动过了。。。
foreach对这个数组做了什么呢?为什么呢?
我要让指针遍历数组,得到如下结果改怎么做呢?

0 => a -- a
1 => b -- b
2 => c -- c
3 => d -- d
4 => e -- e
天蓬老师
天蓬老师

欢迎选择我的课程,让我们一起见证您的进步~~

全部回复(1)
巴扎黑

php的所有变量实际上是用一个struct zval来表示的。

/* Zend/zend.h */
typedef struct _zval_struct zval;
typedef union _zvalue_value {
    long lval;                  /* long value */
    double dval;                /* double value */
    struct {
        char *val;
        int len;
    } str;
    HashTable *ht;              /* hash table value */
    zend_object_value obj;
} zvalue_value;

struct _zval_struct {
    /* Variable information */
    zvalue_value value;     /* value */
    zend_uint refcount;
    zend_uchar type;    /* active type */
    zend_uchar is_ref;
};

而数组就是其中的"HashTable *ht",实际上就是一个哈希表(Hash Table),表中的所有元素同时又组成一个双向链表,它的定义为:

/* Zend/zend_hash.h */
typedef struct _hashtable {
    uint nTableSize;
    uint nTableMask;
    uint nNumOfElements;
    ulong nNextFreeElement;
    Bucket *pInternalPointer;   /* Used for element traversal */
    Bucket *pListHead;          
    Bucket *pListTail;          
    Bucket **arBuckets;
    dtor_func_t pDestructor;
    zend_bool persistent;
    unsigned char nApplyCount;
    zend_bool bApplyProtection;
#if ZEND_DEBUG
    int inconsistent;
#endif
} HashTable;

这里有一个 Bucket *pInternalPointer ,就是被reset/current/next等函数用来遍历数组保存位置状态的。Bucket的实现如下,可以看到这就是个赤裸裸的链表节点。

typedef struct bucket {
    ulong h;                        /* Used for numeric indexing */
    uint nKeyLength;
    void *pData;
    void *pDataPtr;
    struct bucket *pListNext;
    struct bucket *pListLast;
    struct bucket *pNext;
    struct bucket *pLast;
    char arKey[1]; /* Must be last element */
} Bucket;

而foreach的实现,则位于 ./Zend/zend_compile.h ,在解释期被flex翻译成由 zend_do_foreach_begin zend_do_foreach_cont zend_do_foreach_end 这三个函数(以及相关代码)组合起来。由于看起来比较晦涩,我就不贴出来了(实际上我也没看太懂),详情可以参考雪候鸟的这篇:深入理解PHP原理之foreach

最后附一段php代码的opcode

<?php
$arr = array(1,2,3);
foreach ($arr as $x) 
    echo $x;  
?>
number of ops:  12
compiled vars:  !0 = $arr, !1 = $x
line  # *  op                   return  operands
-----------------------------------------------------
2     0  >   INIT_ARRAY           ~0      1         用1初始化数组
      1      ADD_ARRAY_ELEMENT    ~0      2         添加个2
      2      ADD_ARRAY_ELEMENT    ~0      3         添加个3
      3      ASSIGN                       !0, ~0    存入$arr
3     4    > FE_RESET             $2      !0, ->10  $2 = FE_RESET($arr), 失败则跳到#10
      5  > > FE_FETCH             $3      $2, ->10  $3 = FE_FETCH($2), 失败则跳到#10
      6  >   ZEND_OP_DATA                           
      7      ASSIGN                       !1, $3    $x = $3
4     8      ECHO                         !1        echo $x
      9    > JMP                          ->5       跳到#5
     10  >   SWITCH_FREE                  $2        释放$2
5    11    > RETURN                       1         返回
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号