Приоритет операций

108
25 января 2021, 10:30

Не могу понять одну странность с приоритетом операторов в C++

void mass(int* der, int* a) 
{ 
cout << der << endl; //der = 0009D870
*der = 5; 
cout << der << endl; //der = 0009D870
*(der++) = 9; //тут проблема
cout << der << endl; //der = 0009D874 (инкрементируется также и при записи *++der)
} 
void main() 
{ 
int* der = new int[2]; 
der[0] = 3; 
der[1] = 3; 
mass(der, &der[1]); 
cout « der[0] « '\n' « der[1]; 
}

Если записать так: *(der++), то сначала происходит разыменование, а потом инкремент, несмотря на скобки. Значение элемента der[0] меняется на 5, а потом на 9. Если записать так: *++der, то происходит сначала инкремент, потом разыменование, значение элемента массива der[0] меняется на 5, а der[1] на 9. Почему так происходит?

Answer 1

Приоритет операций в языках С и С++ не имеет прямого отношения к порядку их выполнения. Приоритет операций описывает лишь группировку между операторами и операндами во внешне неоднозначных случаях, т.е. отвечает на вопрос о том, какой оператор относится к какому оператору.

(Формально в С и С++ нет никакого "приоритета операций", а есть только грамматика, которая и задает такую группировку. Так называемый приоритет операций - это лишь упрощенный и более удобный способ запоминания этой группировки.)

Вы также наделяете термин выполняется некоей атомарностью, которой у него на самом нет. У каждого оператора, у каждого подвыражения в С и С++ обычно есть результат и потенциально есть побочные эффекты. Вычисление результата оператора (value computation) - это одна часть процесса выполнения оператора, а материализация его побочных эффектов - это другая часть процесса его выполнения. Эти части выполняются в общем случае независимо друг от друга, в совершено разные моменты времени. То есть выполнение оператора в полном объеме может быть "размазано" по всему процессу вычисления полного выражения.

Если записать так: *(der++), то сначала происходит разыменование, а потом инкремент, несмотря на скобки.

Здесь написана какая-то ерунда. Никакого "сначала происходит разыменование" тут нет и быть не может.

Из-за скобок никакой внешней неоднозначности тут нет: ++ относится к der, а * относится к результату ++. Сначала вычисляется именно результат ++. Так как это постфиксный ++, его результатом должно быть старое значение der. Что вы и наблюдаете.

И совершенно не важно, произошло ли уже изменение значения переменной der в момент применения оператора *. Может произошло, а может нет. Главное, что ++ в качестве результата вернул старое значение и именно старое значение попало на вход оператору *. Это все, что вас должно беспокоить: * применяется к старому значению der. Всякие "сначала" и "потом" вас беспокоить не должны вообще.

Скобки тут ни на что не влияют. Их можно убрать и тогда для правильного понимания внешне неоднозначного выражения придется применить приоритеты. Так как постфиксные операторы всегда обладают бОльшим приоритетом, чем префиксные, выражение *der++ разбирается как *(der++).

Если записать так: *++der, то происходит сначала инкремент, потом разыменование

В этом выражении неоднозначности нет и приоритеты никакой роли не играют вообще. ++ относится к der, а * применен к результату ++.

Опять же, важно тут не то, что выполняется первым, а что вторым, а только то, что результат префиксного ++ - это новое значение der. То есть * должен применяться к увеличенному на 1 значению der.

Здесь, кстати, языки С и С++ формально ведут себя по-разному. Язык С++ гарантирует, что когда оператор * получает на вход увеличенное значение der, сама переменная der тоже уже получила новое значение. А вот язык С такого не гарантирует: оператор * тоже получает на вход увеличенное значение der, но вот было ли к этому моменту уже выполнена модификация самой перменной der - не оговаривается.

Answer 2
int a[3]{};
int* der = a;  //der содержит адрес первого элемента массива
int* p = der++; 

указатель p тоже содержит адрес первого элемента, но der уже(сразу после выражения) указывает на второй элемент

int* q = ++p;

q указывает на второй элемент, т.е. q == der , так что тут скобки роли не играют, дело в результате постфиксного и префиксного инкремента

READ ALSO
инициализация int[] по умолчанию

инициализация int[] по умолчанию

Наткнулся на интересную особенность при инициализации массива int

156
Как портировать код qt Window в linux?

Как портировать код qt Window в linux?

Есть одноплатный компьютер для него написал код с графикой использую qtКод писался на Windows как перенести его на Linux, SDK qt на Linux установил но как...

159
Узнать, является ли поток бинарным

Узнать, является ли поток бинарным

Предположим, что у меня есть поток для записи в файл ofstream

112
ISO C++ forbids comparison between pointer and integer [-fpermissive]

ISO C++ forbids comparison between pointer and integer [-fpermissive]

Проблема заключается в том, что код спокойно выполняется на онлайн gcc компиляторах и MSVS, а у меня - нетОшибку видит в строках while (string[i] != '\0') и ошибкуerror:...

91