> 백엔드 개발 > PHP 튜토리얼 > php如何移动数组内部指针

php如何移动数组内部指针

WBOY
풀어 주다: 2016-06-06 20:51:09
원래의
1225명이 탐색했습니다.

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
로그인 후 복사
로그인 후 복사

回复内容:

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
로그인 후 복사
로그인 후 복사

这个问题我在bugs.php.net上回答过: https://bugs.php.net/bug.php?id=63752

1. at the beginning of foreach: ZEND_FE_RESET which increase the refoucnt of $a
2. then FE_FETCH, reset internal pointer of $a
3. then current, current declared to accept a reference, but $a is not a ref and refcount > 1 , then -> separation

PHP 的数组指针操作函数:

  1. pos() http://cn2.php.net/manual/en/function...
  2. end() http://cn2.php.net/manual/en/function...
  3. prev() http://cn2.php.net/manual/en/function...
  4. next() http://cn2.php.net/manual/en/function...
  5. each() http://cn2.php.net/manual/en/function...
  6. reset() http://cn2.php.net/manual/en/function...
  7. current() http://cn2.php.net/manual/en/function...
"Also note that foreach operates on a copy of the specified array, not the array itself, therefore the array pointer is not modified as with the each() construct and changes to the array element returned are not reflected in the original array."
http://cn2.php.net/manual/en/control-...

foreach() 操作原始数组的一个拷贝,如果需要移动指针,使用 while 结构加上 each() 来实现。

$arr = array ('a', 'b', 'c', 'd', 'e');
reset($arr);
while (list($k, $v) = each($arr)) {
    # 当前指针已经被指向了下一位
    $curr = current($arr);
    echo "{$k} => {$v} -- {$curr}\n";
}
로그인 후 복사

参见 http://cn2.php.net/manual/en/function... 的 example #2。

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.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿