С наступающим Новым годом! Форум программистов, компьютерный форум, киберфорум
Наши страницы
PHP для начинающих
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.75/4: Рейтинг темы: голосов - 4, средняя оценка - 4.75
полудух
155 / 168 / 46
Регистрация: 15.03.2016
Сообщений: 1,064
1

странный баг с передачей массива в ф-ю по ссылке

25.05.2016, 21:24. Просмотров 764. Ответов 16

есть такой простейший код:
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$data = array(
    array('a','b','c'),
    array('d','e','f'),
    array('g','h','i'),
);
 
func($data);
 
function func(&$data)
{
    $cnt = 0;
 
    foreach ($data as $key=>$arr)
    {
        // без этого сожрёт всю память и подвесит комп
        if ($cnt>10) {echo"облом\n";break;}
 
        // и вот тут я хочу создать новый key=>val, а получаю бесконечный цикл с проеданием памяти
        $data{$key.'a'} = "new data";
 
        $cnt++;
    }
}
при попытке создать $data{$key.'a'} попадаю в бесконечный цикл...

можно без проблем создать $data{'newkey'}, т.е. без переменной, но мне надо присутствие $key
также всё работает, если убрать ссылку с &$data при передаче в ф-ю, но это опять не то

что я делаю не так?
0
Лучшие ответы (1)
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
25.05.2016, 21:24
Ответы с готовыми решениями:

Хочу разобраться с передачей и приемом переменной по ссылке
Здравствуйте. На странице существует таблица при нажатии на которой ссылка...

foreach по ссылке - странный результат
Здравствуйте, не могу понять почему выводится A B C D D Объясните пожалуйста...

Передача массива по ссылке
Здравствуйте. Подскажите, как передать массив по ссылке. массив metro...

Вывод массива по ссылке
Как вывести массив по ссылке (не по значению)? для проверки используется...

Передача массива по ссылке
Здравствуйте. Помню где-то читал что можно передавать массивы по страницам...

16
Jewbacabra
Эксперт PHP
3598 / 2986 / 1325
Регистрация: 24.04.2014
Сообщений: 9,053
25.05.2016, 22:29 2
Цитата Сообщение от полудух Посмотреть сообщение
что я делаю не так?
Ты пытаешься пройти по всем элементам $data, на каждой итерации добавляя по 1 новому элементу.
0
полудух
155 / 168 / 46
Регистрация: 15.03.2016
Сообщений: 1,064
25.05.2016, 22:58  [ТС] 3
Цитата Сообщение от Jewbacabra Посмотреть сообщение
Ты пытаешься пройти по всем элементам $data, на каждой итерации добавляя по 1 новому элементу.
ну и что, без ссылки же всё работает
получается без ссылки там фиксированное кол-во эл-тов, а со ссылкой они каждый раз пересчитываются
как так?
0
pav1uxa
1858 / 1712 / 802
Регистрация: 23.01.2014
Сообщений: 6,082
Завершенные тесты: 1
25.05.2016, 23:49 4

Не по теме:

Цитата Сообщение от полудух Посмотреть сообщение
странный баг
Да почему люди всегда думают, что если скрипт работает не так как они ожидают - это баг :D


Цитата Сообщение от полудух Посмотреть сообщение
как так?
Для начала нужно определиться - Вам просто интересно почему так происходит, или все-таки Вам для дела этот код нужен? Если для дела - то объясните что именно Вы хотите сделать, уверен - тут подскажут вариант получше.

Если же возвращаться к вопросу "а почему так": когда Вы передаете переменную по ссылке, в конструкцию foreach() передается так же ссылка на глобальный массив $data. То есть $data глобальная, $data в области видимости функции и $data, которую "итерирует" foreach - это один и тот же объект. Соответственно все изменения массива меняют ход работы конструкции foreach (это будет понятнее, если разобраться как именно работает foreach, какие методы объекта вызывает и т.д.). Когда же передаете переменную не по ссылке - передается копия массива. В свою очередь, когда эту копию передаете в конструкцию foreach - туда передастся опять же копия от копии. Таким образом глобальная $data, $data в области видимости функции, и $data, которую перебирает foreach - Это 3 разные переменные. И изменения $data в области видимости функции никак не влияют на ход контрукции foreach...
0
Jewbacabra
Эксперт PHP
3598 / 2986 / 1325
Регистрация: 24.04.2014
Сообщений: 9,053
25.05.2016, 23:58 5
Что смог найти по этому вопросу: https://nikic.github.io/2011/11/11/P...each-copy.html

foreach will copy the array structure if and only if the iterated array is not referenced and has a refcount > 1
Значения php массивов храняться в такой структуре:
C
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
typedef struct _hashtable {
    uint nTableSize;
    uint nTableMask;
    uint nNumOfElements;
    ulong nNextFreeElement;
    Bucket *pInternalPointer;
    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;
pInternalPointer pListHead pListTail - различные внутренние указатели, используются в foreach
Так же у значени php переменно есть refcount - сколько еще переменных ссылается на это значение (не путать с php ссылками &). В случае, когда refcount > 1 и массив не передан по ссылке, создается копия данной структуры (при этом сами значения не копируются)
0
полудух
155 / 168 / 46
Регистрация: 15.03.2016
Сообщений: 1,064
26.05.2016, 00:08  [ТС] 6
Цитата Сообщение от pav1uxa Посмотреть сообщение
Да почему люди всегда думают, что если скрипт работает не так как они ожидают - это баг
потому что когда уже написаны сотни тысяч строк кода, остаются практически только баги )

Цитата Сообщение от Jewbacabra Посмотреть сообщение
foreach will copy the array structure if and only if the iterated array is not referenced and has a refcount > 1
ну правильно, мне так и надо, чтобы оригинальный массив правился
суть бага в чём: этот же массив, но без ф-и или без ссылки, если его взять и начать заполнять доп ключами, он же не будет БЕСКОНЕЧНО заполняться то
а когда он приходит по ссылке в ф-ю, то почему-то начинает с каждым новым ключём ПЕРЕСЧИТЫВАТЬ массив и пихать в текущий цикл foreach новые ключи (точнее массив не пересчитывает, судя по времени, а просто пихает и тут же увеличивает итерацию на 1)
два разных поведения при почти одинаковом положении дел - что это, если не баг?

зы:
Цитата Сообщение от pav1uxa Посмотреть сообщение
(это будет понятнее, если разобраться как именно работает foreach, какие методы объекта вызывает и т.д.)
не понял откуда классы тут взялись
0
Jewbacabra
Эксперт PHP
3598 / 2986 / 1325
Регистрация: 24.04.2014
Сообщений: 9,053
26.05.2016, 00:19 7
Лучший ответ Сообщение было отмечено полудух как решение

Решение

Цитата Сообщение от полудух Посмотреть сообщение
суть бага в чём: этот же массив, но без ф-и или без ссылки, если его взять и начать заполнять доп ключами, он же не будет БЕСКОНЕЧНО заполняться то
а когда он приходит по ссылке в ф-ю, то почему-то начинает с каждым новым ключём ПЕРЕСЧИТЫВАТЬ массив и пихать в текущий цикл foreach новые ключи (точнее массив не пересчитывает, судя по времени, а просто пихает и тут же увеличивает итерацию на 1)
два разных поведения при почти одинаковом положении дел - что это, если не баг?
Это фича, точнее особенности реализации. В php ссылки достаточно опасная вещь, является источником неприятных ошибок, использовать ссылки надо очень аккуратно
При итерировании массива создается копия внутренней структуры массива (в частности указатели на первый, последний и текущий элемент массива), а в случае с ссылкой никакой копии не создается, и поэтоому все изменения видны "на лету"
1
полудух
155 / 168 / 46
Регистрация: 15.03.2016
Сообщений: 1,064
26.05.2016, 00:31  [ТС] 8
ну понятно
"особенности реализации"
в общем сделал так:
PHP
1
2
3
4
5
6
$data = func($data);
function func($data)
{
    ...
    return $data;
}
а что со ссылкой то (без размножения всего массива) никак нельзя чтоли получить тоже самое?
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
array (
  0 => 
  array (
    0 => 'a',
    1 => 'b',
    2 => 'c',
  ),
  1 => 
  array (
    0 => 'd',
    1 => 'e',
    2 => 'f',
  ),
  2 => 
  array (
    0 => 'g',
    1 => 'h',
    2 => 'i',
  ),
  '0a' => 'new data',
  '1a' => 'new data',
  '2a' => 'new data',
)
0
pav1uxa
1858 / 1712 / 802
Регистрация: 23.01.2014
Сообщений: 6,082
Завершенные тесты: 1
26.05.2016, 00:56 9

Не по теме:

Цитата Сообщение от полудух Посмотреть сообщение
не понял откуда классы тут взялись
Да, это так, для примера... http://php.net/manual/ru/class.iteratoraggregate.php http://php.net/manual/ru/class.iterator.php


Цитата Сообщение от полудух Посмотреть сообщение
потому что когда уже написаны сотни тысяч строк кода, остаются практически только баги )
никаких багов...
Цитата Сообщение от полудух Посмотреть сообщение
а что со ссылкой то (без размножения всего массива) никак нельзя чтоли получить тоже самое?
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?php
function func(&$a)
{
    $n = count($a);
    for ($i = 0; $i < $n; ++$i)
        $a[$i.'a'] = "new data";
    return $a;
}
 
$a = array(
    array('a','b','c'),
    array('d','e','f'),
    array('g','h','i'),
);
 
func($a);
 
print_r($a);
Код
Array
(
    [0] => Array
        (
            [0] => a
            [1] => b
            [2] => c
        )

    [1] => Array
        (
            [0] => d
            [1] => e
            [2] => f
        )

    [2] => Array
        (
            [0] => g
            [1] => h
            [2] => i
        )

    [0a] => new data
    [1a] => new data
    [2a] => new data
)
0
полудух
155 / 168 / 46
Регистрация: 15.03.2016
Сообщений: 1,064
26.05.2016, 00:58  [ТС] 10
не, у меня в реале там ключи-стринги
0
pav1uxa
1858 / 1712 / 802
Регистрация: 23.01.2014
Сообщений: 6,082
Завершенные тесты: 1
26.05.2016, 00:59 11
а у меня что
0
полудух
155 / 168 / 46
Регистрация: 15.03.2016
Сообщений: 1,064
26.05.2016, 01:02  [ТС] 12
Цитата Сообщение от pav1uxa Посмотреть сообщение
а у меня что
$i на цифрах
у меня не 012, а 'a','b','c' например
не прокатит for, а то и не спрашивал бы
0
pav1uxa
1858 / 1712 / 802
Регистрация: 23.01.2014
Сообщений: 6,082
Завершенные тесты: 1
26.05.2016, 01:03 13
Цитата Сообщение от полудух Посмотреть сообщение
$i на цифрах
у меня не 012, а 'a','b','c' например
не прокатит for, а то и не спрашивал бы
Не понимаю, я сделал на выходе массив точь-в-точь как тут:
Цитата Сообщение от полудух Посмотреть сообщение
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
array (
* 0 => 
* array (
* * 0 => 'a',
* * 1 => 'b',
* * 2 => 'c',
* ),
* 1 => 
* array (
* * 0 => 'd',
* * 1 => 'e',
* * 2 => 'f',
* ),
* 2 => 
* array (
* * 0 => 'g',
* * 1 => 'h',
* * 2 => 'i',
* ),
* '0a' => 'new data',
* '1a' => 'new data',
* '2a' => 'new data',
)
Но Вас это все равно не устраивает. Определитесь что Вам нужно то.
0
полудух
155 / 168 / 46
Регистрация: 15.03.2016
Сообщений: 1,064
26.05.2016, 01:05  [ТС] 14
да говорю же пример это
в реале он такой:
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
array (
  'a' => 
  array (
    'a' => 'a',
    'b' => 'b',
    'c' => 'c',
  ),
  'b' => 
  array (
    'a' => 'd',
    'b' => 'e',
    'c' => 'f',
  ),
  'c' => 
  array (
    'a' => 'g',
    'b' => 'h',
    'c' => 'i',
  )
);
0
pav1uxa
1858 / 1712 / 802
Регистрация: 23.01.2014
Сообщений: 6,082
Завершенные тесты: 1
26.05.2016, 01:08 15
Да и через foreach() этот пример можно точно также написать, разницы никакой:
PHP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
function func(&$a)
{
    $b = array();
    foreach ($a as $k => $v)
        $b[$k.'a'] = "new data";
    foreach ($b as $k => $v)
        $a[$k] = $v;
    return $a;
}
 
$a = array(
    array('a','b','c'),
    array('d','e','f'),
    array('g','h','i'),
);
 
func($a); 
 
var_export($a);
Код
array (
  0 => 
  array (
    0 => 'a',
    1 => 'b',
    2 => 'c',
  ),
  1 => 
  array (
    0 => 'd',
    1 => 'e',
    2 => 'f',
  ),
  2 => 
  array (
    0 => 'g',
    1 => 'h',
    2 => 'i',
  ),
  '0a' => 'new data',
  '1a' => 'new data',
  '2a' => 'new data',
)
Добавлено через 1 минуту
Цитата Сообщение от полудух Посмотреть сообщение
в реале он такой:
Господи, ну так напишите уже какой он в реале и какой должен быть не выходе, в реале на выходе а не например на выходе, без всяких "но". Сколько можно в угадалки играть)
0
полудух
155 / 168 / 46
Регистрация: 15.03.2016
Сообщений: 1,064
26.05.2016, 01:11  [ТС] 16
Цитата Сообщение от pav1uxa Посмотреть сообщение
PHP
1
2
3
4
5
6
7
8
9
function func(&$a)
{
    $b = array();
    foreach ($a as $k => $v)
        $b[$k.'a'] = "new data";
    foreach ($b as $k => $v)
        $a[$k] = $v;
    return $a;
}
вы его перебрали тут 2.5 раза
получилось ещё хуже, чем у меня
Цитата Сообщение от полудух Посмотреть сообщение
PHP
1
2
3
4
5
6
$data = func($data);
function func($data)
{
    ...
    return $data;
}
Цитата Сообщение от pav1uxa Посмотреть сообщение
Господи, ну так напишите уже какой он в реале и какой должен быть не выходе, без всяких "но". Сколько можно в угадалки играть)
в 3й раз чтоли?
короче я уже понял, что нельзя, фиг с ним
0
pav1uxa
26.05.2016, 01:14     странный баг с передачей массива в ф-ю по ссылке
  #17

Не по теме:

жесткий троллинг :D

0
26.05.2016, 01:14
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
26.05.2016, 01:14
Привет! Вот еще темы с ответами:

Передача элемента массива по ссылке
Была задача рандомно заменить каждый четный элемент массива. Как в данном...

Обращение к элементу массива по символической ссылке
Привет. Такая задача: есть массив $array = array( 'a' =&gt; array( ...

Странный баг
После проблем с вирусом svchost.exe после перезагрузки Lotus Domino все сервисы...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
17
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru