Как сделать инкрементацию числа
PHP поддерживает префиксные и постфиксные операторы инкремента и декремента в стиле C.
Замечание: Операторы инкремента/декремента влияют только на числа и строки. Массивы, объекты, булевы значения и ресурсы не будут изменены. Декремент null также не даст никакого эффекта, однако инкремент даст значение 1 .
| Пример | Название | Действие |
|---|---|---|
| ++$a | Префиксный инкремент | Увеличивает $a на единицу, затем возвращает значение $a . |
| $a++ | Постфиксный инкремент | Возвращает значение $a , затем увеличивает $a на единицу. |
| —$a | Префиксный декремент | Уменьшает $a на единицу, затем возвращает значение $a . |
| $a— | Постфиксный декремент | Возвращает значение $a , затем уменьшает $a на единицу. |
Приведём пример простого скрипта:
<?php
echo «<h3>Постфиксный инкремент</h3>» ;
$a = 5 ;
echo «Должно быть 5: » . $a ++ . «<br />\n» ;
echo «Должно быть 6: » . $a . «<br />\n» ;
echo «<h3>Префиксный инкремент</h3>» ;
$a = 5 ;
echo «Должно быть 6: » . ++ $a . «<br />\n» ;
echo «Должно быть 6: » . $a . «<br />\n» ;
echo «<h3>Постфиксный декремент</h3>» ;
$a = 5 ;
echo «Должно быть 5: » . $a — . «<br />\n» ;
echo «Должно быть 4: » . $a . «<br />\n» ;
echo «<h3>Префиксный декремент</h3>» ;
$a = 5 ;
echo «Должно быть 4: » . — $a . «<br />\n» ;
echo «Должно быть 4: » . $a . «<br />\n» ;
?>
PHP следует соглашениям Perl (в отличие от С) касательно выполнения арифметических операций с символьными переменными. Например, в PHP и Perl $a = 'Z'; $a++; присвоит $a значение 'AA' , в то время как в C a = 'Z'; a++; присвоит a значение '[' (ASCII-значение 'Z' равно 90, а ASCII-значение '[' равно 91). Следует учесть, что к символьным переменным можно применять операцию инкремента, в то время как операцию декремента применять нельзя, кроме того, поддерживаются только ASCII-символы (a-z, A-Z и 0-9). Попытка инкремента/декремента других символьных переменных не будет иметь никакого эффекта, исходная строка останется неизменной.
Пример #1 Арифметические операции с символьными переменными
Результат выполнения данного примера:
Инкрементирование или декрементирование булевых переменных не приводит ни к какому результату.
User Contributed Notes 10 notes
$a=»9D9″; var_dump(++$a); => string(3) «9E0»
but counting onwards from there
$a=»9E0″; var_dump(++$a); => float(10)
this is due to «9E0» being interpreted as a string representation of the float constant 9E0 (or 9e0), and thus evalutes to 9 * 10^0 = 9 (in a float context)
Regarding character incrementing and PHP following Perl’s convention with character operations.
Actually i found that there is a difference, and incrementing and decrementing unfortunately does not yield the reverse, expected results.
For example, the following piece of code:
<?php
echo ‘== Alphabets ==’ . PHP_EOL ;
$s = ‘W’ ;
for ( $n = 0 ; $n < 10 ; $n ++) <
echo ++ $s . ‘ ‘ ;
>
echo PHP_EOL ;
for ( $n = 10 ; $n > 0 ; $n —) <
echo (— $s ) . ‘ ‘ ;
>
?>
Will output:
== Alphabets ==
X Y Z AA AB AC AD AE AF AG
AG AG AG AG AG AG AG AG AG AG
Please note that the decrement operator has no effect on the character or string.
On the other hand, in Perl, the similar script:
X Y Z AA AB AC AD AE AF AG
-1 -2 -3 -4 -5 -6 -7 -8 -9 -10
Note that the ++ and — don’t convert a boolean to an int. The following code will loop forever.
function a($start_index) <
for($i = $start_index; $i < 10; $i++) echo «\$i = $i\n»;
>
This behavior is, of course, very different from that in C. Had me pulling out my hair for a while.
Rule for Increment and decrement:
At some moment we could be confused with increment and decrement in various cases. To avoid such cases, let us follow certain logical rule behind to get successful results with out mess.
<?php
$n = 3 ;
echo $n — + — $n ;
echo «<br/>» ;
echo $n ;
?>
1. Postfix form of ++,— operator follows the rule [ use-then-change ],
2. Prefix form (++x,—x) follows the rule [ change-then-use ].
Solution based on the rule:
Step 1:
use then change $n— use is 3 and change is 2
Step 2.
change then use —$n change is 2 and use is 1
Step 3.
use + use = (3 + 1) = 4
Courtesy : stackoverflow : Sunil Dhillon : 4686665
When using the ++ operator by itself on a variable, ++$var is faster than $var++ and uses slightly less memory (in my experiments). It would seem like this could be optimized in the language during runtime (if $var++ is the only thing in the whole statement, it could be treated as ++$var).
I conducted many tests (I believe to be fair), and here’s one of the results:
$i++ took 8.47515535355 seconds and 2360 bytes
++$i took 7.80081486702 seconds and 2160 bytes
Here’s my code. If anyone sees a bias in it, tell me. I conducted it many times, each time going through a loop one million iterations and doing each test 10 — 15 times (10 — 15 million uses of the ++ operator).
ini_set ( ‘MAX_EXEC_TIME’ , 120 );
ob_start ( );
$num_tests = 10 ;
$startFirst = $startSecond = $endFirst = $endSecond = $startFirstMemory = $endFirstMemory = $startSecondMemory = $endSecondMemory = $someVal = 0 ;
$times = array( ‘$i++’ => array( ‘time’ => 0 , ‘memory’ => 0 ), ‘++$i’ => array( ‘total’ => 0 , ‘memory’ => 0 ) );
for( $j = 0 ; $j < $num_tests ; ++ $j )
<
for( $i = 0 , $startFirstMemory = memory_get_usage ( ), $startFirst = microtime ( true ); $i < 10000000 ; $i ++ )< $someval = 2 ; >
$endFirstMemory = memory_get_usage ( );
$endFirst = microtime ( true );
for( $i = 0 , $startSecondMemory = memory_get_usage ( ), $startSecond = microtime ( true ); $i < 10000000 ; ++ $i )< $someval = 2 ; >
$endSecondMemory = memory_get_usage ( );
$endSecond = microtime ( true );
$times [ ‘$i++’ ][ $j ] = array( ‘startTime’ => $startFirst , ‘endTime’ => $endFirst , ‘startMemory’ => $startFirstMemory , ‘endMemory’ => $endFirstMemory );
$times [ ‘++$i’ ][ $j ] = array( ‘startTime’ => $startSecond , ‘endTime’ => $endSecond , ‘startMemory’ => $startSecondMemory , ‘endMemory’ => $endSecondMemory );
>
for( $i = 0 ; $i < $num_tests ; ++ $i )
<
$times [ ‘$i++’ ][ ‘time’ ] += ( $times [ ‘$i++’ ][ $i ][ ‘endTime’ ] — $times [ ‘$i++’ ][ $i ][ ‘startTime’ ] );
$times [ ‘++$i’ ][ ‘time’ ] += ( $times [ ‘++$i’ ][ $i ][ ‘endTime’ ] — $times [ ‘++$i’ ][ $i ][ ‘startTime’ ] );
$times [ ‘$i++’ ][ ‘memory’ ] += ( $times [ ‘$i++’ ][ $i ][ ‘endMemory’ ] — $times [ ‘$i++’ ][ $i ][ ‘startMemory’ ] );
$times [ ‘++$i’ ][ ‘memory’ ] += ( $times [ ‘++$i’ ][ $i ][ ‘endMemory’ ] — $times [ ‘++$i’ ][ $i ][ ‘startMemory’ ] );
>
echo ‘There were ‘ . $num_tests . ‘ tests conducted, here\’s the totals<br /><br />
$i++ took ‘ . $times [ ‘$i++’ ][ ‘time’ ] . ‘ seconds and ‘ . $times [ ‘$i++’ ][ ‘memory’ ] . ‘ bytes<br />
++$i took ‘ . $times [ ‘++$i’ ][ ‘time’ ] . ‘ seconds and ‘ . $times [ ‘++$i’ ][ ‘memory’ ] . ‘ bytes’ ;
Инкременты и декременты
В данной статье активно используется термин «операнд».
Так что такое операнд?
Операнд — это величина, над которой операторы могут выполнять определенные действия.
О! А что такое оператор?
Оператор — это специальный символ, выполняющий определенную операцию с одним, двумя или тремя операндами, а затем возвращающий результат. Пример: 1+2. В этом выражении числа 1 и 2 являются операндами, а знак сложения (+) — это оператор. Инкременты и декременты также являются двумя операторами.
Так-так! Что такое инкременты и декременты?
Инкременты (++). Оператор инкремент (++) увеличивает операнд или прибавляет к нему единицу (+1), а затем возвращает значение.
Декременты (- -). Оператор декремент (- -) уменьшает операнд или вычитает из него единицу (-1), а затем возвращает значение. Инкременты и декременты используют постфиксы (х++, х- -) и префиксы (++х,- -х).
Отлично! А что такое постфикс и префикс?
Постфикс. Этот оператор используется после операнда. Он возвращает значение до приращения/убавления. Для инкрементов операндом является х:
Для декрементов операндом считается у:
Префикс. Этот оператор используется перед операндом. Он возвращает значение после прибавления/вычитания. Для инкрементов операндом является х:
Для декрементов операндом считается у:
Отлично, теперь понятно! А можно коротко и по существу?
При использовании (++) или (- -) перед переменной, происходит выполнение операции приращения/вычитания и только затем возврат значения. Добавление (++) или (- -) после переменной сначала возвращает значение, а затем выполняет операцию приращения/вычитания!
Программирование на C, C# и Java
Уроки программирования, алгоритмы, статьи, исходники, примеры программ и полезные советы
ОСТОРОЖНО МОШЕННИКИ! В последнее время в социальных сетях участились случаи предложения помощи в написании программ от лиц, прикрывающихся сайтом vscode.ru. Мы никогда не пишем первыми и не размещаем никакие материалы в посторонних группах ВК. Для связи с нами используйте исключительно эти контакты: vscoderu@yandex.ru, https://vk.com/vscode
Инкремент и декремент
Инкремент – это операция в языках программирования, которая увеличивает переменную на единицу, если переменная числовая и возвращает следующий символ из таблицы символов, если переменная символьного типа. При этом переменная не должна принадлежать типу данных const (т.е. константе).
Операторы инкремента записывается как два плюса: ++
Существуют два вида инкрементов: преинкремент (или префиксный инкремент) и постинкремент (или постфиксный инкремент).
В синтаксисе префиксный инкремент ставится перед необходимой переменной, а постфиксный, соответственно, после.
Главное различие между ними, что при использовании операции преинкремента значение переменной сначала увеличивается на 1, а затем используется в выражении, к которому относится данная переменная. А при использовании операции постинкремента значение переменной сначала используется в выражении, а потом увеличивается на 1.
Более подробно и с примерами про это будет рассказано ниже.
Декремент – это подобная инкременту операция, с той лишь разницей, что она уменьшает числовую переменную на единицу, а для символьной переменной выбирает предшествующий ей символ из таблицы символов. Декремент также не работает с константным типом данных и также имеет два вида: предекремент (префиксный декремент) и постдекремент (постфиксный декремент).
Эти виды декрементов работают абсолютно так же, как и инкременты с той лишь разницей, что происходит уменьшение значения на единицу. Префиксный декремент уменьшает переменную до её использования в других выражениях, а постфиксный декремент, наоборот, уменьшает переменную только после того, как она будет использована в выражении.
Декремент записывается как два минуса: — — . В коде предекремент ставится перед переменной, а постдекремент – после.
Обе эти операции были созданы для удобства пользователей – для сокращения количества кода и для уменьшения времени его написания:
Инкремент в PHP

Возьмите переменную и увеличьте её на 1. Звучит просто, верно? Ну… С точки зрения PHP-разработчика, наверное, да. Но так ли это на самом деле? Здесь могут возникнуть некоторые трудности. Существует несколько способов инкрементировать значения, они могут выглядеть равноценными, но под капотом PHP работают по-разному, что может привести к, так сказать, интересным результатам.
Рассмотрим три примера добавления единицы к переменной:
Код разный, но в каждом случае значение переменной увеличивается. А какой будет результат?
Интуитивно все три способа выглядят равнозначно. То есть для инкрементирования можно использовать как $a++ , так и $a += 1 . Но давайте рассмотрим другой пример:
Наверняка многие из вас не ожидали такого результата! Может быть, кто-то уже знал, что добавление к строковой переменной приводит к изменению набора символов, но два int(1) ? Откуда они взялись? С точки зрения PHP-разработчика это выглядит очень несогласованно, и выходит, что наши три способа инкрементирования неравнозначны. Давайте посмотрим, что происходит в недрах PHP при выполнении кода.
Байт-код
Во время запуска PHP-скрипта ваш код сначала компилируется в промежуточный формат — байт-код. Этот факт опровергает мнение, что PHP по-настоящему интерпретируемый язык, — интерпретируется байт-код, а не исходный код PHP.
Приведённый выше код преобразуется в такой байт-код:
Вы легко можете создать такие опкоды самостоятельно, воспользовавшись дебаггером VLD или онлайн-сервисом 3v4l.org. Не думайте о том, что это всё означает. Если избавиться от неинтересных вещей, то останутся только эти строки:
Таким образом, $a++ превращается в два опкода ( POST_INC и FREE ), $a += 1 — в один ( ASSIGN_ADD ) и $a = $a + 1 тоже в два. Обратите внимание, что во всех трёх случаях получились разные опкоды, что уже подразумевает разное исполнение PHP.
Оператор унарного инкрементирования
Рассмотрим первый способ инкрементирования — унарный оператор ( $a++ ). Этот PHP-код преобразуется в опкод POST_INC . К слову, PRE_INC получается из ++$a , и вам нужно знать разницу между ними. Второй опкод — FREE — очищает результат после POST_INC , потому что мы не используем его возвращаемое значение: POST_INC на месте изменяет актуальный операнд. В данном случае можно проигнорировать этот опкод.
Причина различия в исполнении этих опкодов кроется в файле zend_vm_def.h , который вы можете найти в исходном С-коде PHP. Это большой заголовочный файл, наполненный макросами, поэтому его не так легко читать, даже если вы знаете С. При вызове опкода POST_INC выполняется содержимое строки 971.
Если коротко, то происходит вот что:
- Проверяется, принадлежит ли переменная ( $a в PHP-коде, которая в байт-коде превращается в !0 ) к типу long . По сути, система проверяет, содержит ли переменная число. Хотя PHP — язык с динамической типизацией, каждая переменная всё же принадлежит к какому-то «типу». Типы могут меняться, как мы увидим далее. Если наша переменная относится к long , то вызывается С-функция fast_increment_function() и происходит возврат к следующему опкоду.
- Если переменная нечисловая, то выполняются базовые проверки на возможность инкрементирования. Например, этого нельзя сделать со строковыми смещениями (string offset) $a = «foobar» ; $a[2]++ , мы получим ошибку.
- Далее проверяется, является ли переменная несуществующим свойством объекта, имеющего волшебные PHP-методы __get и __set . Если это так, то с помощью __get извлекается правильное значение, вызывается fast_increment_function() и значение сохраняется с помощью вызова метода __set . Эти методы вызываются из С, а не из PHP.
- Наконец, если переменная не является свойством, то просто вызывается increment_function() .
fast_increment_function()
Функция fast_increment_function() относится к zend-операторам, и её задачей является максимально быстрое инкрементирование конкретной переменной.
Если переменная относится к типу long , то для её инкрементирования используется очень быстрый ассемблерный код. Если значение достигло максимального числа типа INT ( LONG_MAX ), то переменная автоматически преобразуется в двойную ( double ). Это самый быстрый способ увеличения числа, поскольку эта часть кода написана на ассемблере. Считается, что компилятор не может оптимизировать С-код лучше, чем ассемблер. Но способ работает только в том случае, если переменная относится к типу long . Иначе будет выполнен редирект на функцию increment_function() . Поскольку инкрементирование (и декрементирование) чаще всего выполняется в очень маленьких внутренних циклах (например, for ), то необходимо делать это как можно быстрее ради сохранения высокой производительности PHP.
increment_function()
Если fast_increment_function() — быстрый способ инкрементировать число, то increment_function — медленный ( slow ) способ. Сценарий процесса тоже зависит от типа переменной.
- Если переменная относится к типу long , то число просто увеличивается (и преобразуется в double при достижении максимального значения, которое уже нельзя хранить в long ). Чаще всего это уже будет сделано с помощью fast_increment_function , но может случиться так, что этой функции всё равно будет передано long , так что и здесь необходима проверка.
- Если переменная относится к типу double , то она просто увеличивается.
- Если переменная относится к типу NULL , то всегда возвращается long 1 .
- Если переменная относится к типу string , то применяется описанная выше магия.
- Если переменная — объект и имеет функциональность оператора internal , то вызывается оператор add для добавления long 1 . Обратите внимание, что это работает только для классов internal , которые вручную определяют эти функции оператора, вы не можете определять операторы объекта в PHP-коде пространства пользователя. Это реализует единственный класс в исходном PHP-коде — GMP . Так что вы можете сделать $a = new gmp(1) + new gmp(3); // gmp(4) . Такая возможность появилась начиная с PHP 5.6, но перегрузка оператора невозможна в PHP напрямую.
- Если переменная относится к какому-то другому типу, то её нельзя инкрементировать и возвращается код сбоя.
Инкрементирование строк
А теперь самое забавное. Работа со строками всегда полна нюансов, но в данном случае происходит вот что.
Во-первых, проверяется, содержит ли строка число. Например, строковая 123 содержит число 123. Такое «строчное число» будет преобразовано в нормальное число типа long (int(123) ). При конвертировании используется несколько уловок:
- Удаляются пробелы.
- Поддерживаются шестнадцатеричные числа ( 0x123 ).
- Не поддерживаются восьмеричные и двоичные числа ( 0123 и b11 ).
- Поддерживается научное представление ( 1E5 ).
- Поддерживаются double .
- Не поддерживаются и не считаются числами части, стоящие в начале или в конце строковой ( 135abc или ab123 ).
Если строковая не может быть преобразована в long или double , то вызывается функция increment_string() .
increment_string()
PHP использует систему инкрементирования наподобие Perl. Если строковая пустая, то просто возвращается string(«1») . В противном случае для инкрементирования строковой применяется система переноса (carry-system).
Начинаем с конца переменной. Если символ от a до z , то он инкрементируется ( a становится b , и т. д.). Если символ z , то меняется на a и «переносится» на одну позицию перед текущей.
То есть: a становится b , ab становится ac (перенос не нужен), az становится ba ( z становится a , a становится b , потому что мы переносим один символ).
То же самое относится и к прописным символам от A до Z , а также к цифрам от 0 до 9 . При инкрементировании 9 превращается в 0 и переносится на предыдущую позицию.
Если мы достигли начала строковой переменной и нужно сделать перенос, то просто добавляется ещё один символ ПЕРЕД всей строковой. Тип тот же, что и у переносимого символа:
Так что при инкрементировании строки невозможно изменить тип каждого символа. Если он был в нижнем регистре, то в нём и останется.
Но будьте осторожны, если станете инкрементировать «число в строке» несколько раз.
При инкрементировании string(«2D9») получится string(«2E0») ( string(«2D9» ) не является числом, поэтому будет выполняться инкрементирование обычной строки). Но при инкрементировании string(«2E0») вы получите уже double(3) , потому что 2E0 — научное представление 2 и она будет преобразована в double , который затем может быть инкрементирован до 3. Так что будьте внимательны с циклами инкрементирования!
Эта система инкрементирования строк также объясняет, почему мы можем инкрементировать “Z” до “AA”, но не можем декрементировать “AA” обратно до “Z”. Декрементируется только последний символ “A”, но что делать с первым? Его тоже надо декрементировать до “Z” с помощью (отрицательного) переноса? А что насчёт “0A”? Оно должно стать Z ? И если да, то при новом инкрементировании мы получим уже AA . Иными словами, мы не можем просто убрать символы во время декрементирования, как мы добавляем их при инкрементировании.
Суммирующий оператор присваивания
Рассмотрим теперь второй пример из начала статьи — суммирующий оператор присваивания ( $a += 1 ). Выглядит аналогично унарному оператору инкремента, но ведёт себя иначе с точки зрения генерируемых опкодов и фактического выполнения. Выражение полностью обрабатывается с помощью zend_binary_assign_op_helper, который после ряда проверок вызывает add_function с двумя операндами: $a и нашим значением int(1) .
add_function()
Метод add_function работает по-разному в зависимости от типов переменных. По большей части он состоит из проверки типов операндов:
- Если они оба относятся к long , то их значения просто увеличиваются (при переполнении преобразуются в double ).
- Если один long , а второй double , то оба преобразуются в double и инкрементируются.
- Если они оба относятся к double , то просто суммируются.
- Если они оба являются массивами, то будут объединены на основе ключей: $a = [ ‘a’, ‘b’ ] + [ ‘c’, ‘d’ ]; . Получится [ ‘a’, ‘b’] , как если бы объединили второй массив, но у них оказались одинаковые ключи. Обратите внимание, что объединение происходит не по значениям, а по ключам.
- Если операнды являются объектами, то проверяется, имеет ли первый из них внутреннюю функциональность оператора (как в случае с методом increment_function() ). У вас не получится сделать так в PHP самостоятельно, это поддерживается только внутренними классами вроде GMP.
zendi_convert_scalar_to_number()
Преобразование скаляра в число зависит от типа скаляра. Обычно всё сводится к одному из следующих алгоритмов:
- Если скаляр — строка, то с помощью is_numeric_string проверяется, содержит ли она число. Если нет, то возвращается int(0) .
- Если скаляр — null или булево false , то возвращается int(0) .
- Если скаляр — булево true , то возвращается int(1) .
- Если скаляр — ресурс (resource), то возвращается цифровое значение номера ресурса (resource number).
- Если скаляр — объект, то делается попытка преобразовать его в long (как и в случае с внутренними операторами, здесь может быть функциональность внутреннего преобразования (internal cast functionality), но она не всегда реализована и доступна только для основных классов, а не для PHP-классов в пространстве пользователя).
Оператор суммы
Это самый простой из всех трёх вариантов. При его выполнении вызывается функция fast_add_function() . Как и fast_increment_function() , она напрямую использует ассемблерный код для увеличения чисел, если оба операнда относятся к long или double . Если это не так, то осуществляется редирект на функцию add_function() , используемую выражением присваивания.
Поскольку и оператор сложения, и суммирующий оператор присваивания используют одну и те же базовую функциональность, то $a = $a + 1 и $a += 1 работают одинаково. Единственное различие заключается в том, что оператор сложения МОЖЕТ выполняться быстро, если оба операнда относятся к long или double . Так что если вы хотите сделать микрооптимизацию, то $a = $a + 1 будет работать быстрее, чем $a += 1 . Не только благодаря fast_add_function() , но и потому, что нам не нужно обрабатывать дополнительный байт-код для сохранения результатов обратно в $a .
Заключение
Инкрементирование значения отличается от простого сложения: add_function преобразует типы в совместимые пары, а increment_function этого не делает. Теперь мы можем объяснить полученные результаты:
Поскольку increment_function не преобразует булево значение (это не число и не строка, которую можно преобразовать в число), то происходит тихий сбой и значение не инкрементируется. Поэтому осталось bool(false) . В случае с add_function делается попытка найти соответствие пары boolean и long , которое не существует. В результате оба значения преобразуются в long : bool(false) становится int(0) , а int(1) остаётся int(1) . Теперь у нас есть пара long & long , поэтому add_function просто суммирует их и получается int(1) . (Вопрос: во что превратится булево true + int(1) ?)
Также мы можем объяснить ещё одну странность:
Поскольку строку не получается преобразовать в число, то выполняется обычное инкрементирование строки. Выражение добавления преобразует строки в long после проверки на наличие чисел. Поскольку их нет, то выполняется конвертирование строки в int(0) и к ней добавляется int(1) .