Часто начинающие программисты задают вопрос о том, почему нельзя инкрементировать или декрементировать переменную, имеющую тип массива (смотрите, к примеру, вопрос на Stackoverflow
C++ array variable increment and decrement)
То есть почему, в частности, данная программа некорректна
#include <iostream>
int main()
{
int a[] = { 1, 2, 3, 4, 5 };
std::cout << a++ << std::endl;
return 0;
}
Часто ответы на этот вопрос содержат общие рассуждения, что такая операция для массивов не имеет смысла, и что массивы - это не указатели.
Однако в представленной выше программе, строго говоря, имеет место попытка инкрементировать именно указатель.
Точный ответ в соответствии со стандартом C++ (и стандартом C) заключается в следующем.
Первое (C++17 Standard, 8.5.1.6 Increment and decrement) - постфиксный инкремент или декремент (как и аналогичные префиксные операции) требуют наличие изменяемой
lvalue.
цитата: |
1 The value of a postfix ++ expression is the value of its operand. [ Note: The value obtained is a copy of the original value — end note ] The operand shall be a modifiable lvalue. The type of the operand shall be an arithmetic type other than cv bool, or a pointer to a complete object type. The value of the operand object is modified by adding 1 to it.... |
|
Второе (C++ 17 Standard, 7.2 Array-to-pointer conversion) - переменная, имеющая тип массива, в выражениях, за редким исключением неявно преобразуется во временный объект, который не является
lvalue, и который имеет тип указателя на первый элемент массива.
цитата: |
1 An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to a prvalue of type “pointer to T”. The temporary materialization conversion (7.4) is applied. The result is a pointer to the first element of the array. |
|
То есть, фактически, в этом выражении
a++
имеет место не инкрементирование исходного массива, а попытка инкрементировать временный указатель, который указывает на первый элемент массива, но который при этом не является
lvalue.
Если же вместо временного указателя, в который неявно преобразуется переменная, имеющая тип массива, использовать
lvalue, имеющего такой же тип указателя, то программа будет корректно компилироваться
#include <iostream>
int main()
{
int a[] = { 1, 2, 3, 4, 5 };
int *p = a;
std::cout << p++ << std::endl;
return 0;
}
В этой демонстрационной программе переменная
a, используемая в качестве инициализатора при объявления указателя
p, также преобразуется во временный объект, который является указателем на первый элемент массива. Однако теперь это значение указателя сохраняется в переменной
p, которая является не временным объектом, а
lvalue. А потому к этой переменной
p можно применить операцию инкремента.