Когда точно постфиксный инкрементный оператор оценен в сложном выражении?

Я должен был найти тот же ответ. Лучший пример, который я нашел, http://www.cssplay.co.uk/menu/tablescroll.html - я нашел, что пример № 2 работал хорошо на меня. Необходимо будет установить высоту внутренней таблицы со Сценарием Java, остальное - CSS.

6
задан Bjoern 16 October 2009 в 09:01
поделиться

5 ответов

Проблема заключается в порядке оценки:
Стандарт C ++ не определяет порядок вычисления подвыражений. Это сделано для того, чтобы компилятор мог быть как можно более агрессивным при оптимизации.

Давайте разберемся:

           a1                        a2
v = ( ( p[ i++ ] & 0xFF ) << 4 | ( p[ i ] & 0xF0000000 ) >> 28;

-----
(1) a1 = p[i]
(2) i  = i + 1 (i++)       after (1)      
(3) a2 = p[i]
(4) t3 = a1 & 0xFF         after (1)
(5) t4 = a2 & 0xF0000000   after (3)
(6) t5 = t3 << 4           after (4)
(7) t6 = t4 >> 28          after (5)
(8) t7 = t5 | t6           after (6) and (7)
(9) v  = t7                after (8)

Теперь компилятор может переупорядочивать таким образом подвыражения, если указанные выше предложения «после» не являются нарушено. Таким образом, одна быстрая простая оптимизация - это переместить 3 вверх на один слот, а затем выполнить удаление общих выражений (1) и (3) (теперь рядом друг с другом), то же самое, и, таким образом, мы можем удалить (3)

Но компилятор не имеет для оптимизации (и, вероятно, лучше меня в этом, и у него есть другие уловки в рукаве). Но вы можете видеть, что значение (a1) всегда будет таким, как вы ожидаете, но значение (a2) будет зависеть от того, в каком порядке компилятор решит выполнять другие подвыражения.

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

short v = ( p[ i++ ] & 0xFF) <<   4;
v |= ( p[ i ] & 0xF0000000 ) >>  28;

-----
(1) a1 = p[i]
(2) i  = i + 1 (i++)       after (1)      
(4) t3 = a1 & 0xFF         after (1)
(6) t5 = t3 << 4           after (4)
(A) v = t5                 after (6)
------ Sequence Point
(3) a2 = p[i]
(5) t4 = a2 & 0xF0000000   after (3)
(7) t6 = t4 >> 28          after (5)
(8) t7 = v | t6            after (7)
(9) v  = t7                after (8)

Здесь все хорошо определено, поскольку запись в i есть подать в суд на месте и не перечитывать в том же выражении.

Простое правило. не используйте операторы ++ или - внутри большего выражения. но я бы избегал использовать эти знания, поскольку большинство людей не очень хорошо знакомы с работой компилятора. Если вы пишете код, использующий трюки с точками последовательности, то кто-то может повторно разложить код, чтобы он выглядел более читабельным, и теперь ваш трюк превратился в неопределенное поведение.

short v = ( p[ i++ ] & 0xFF) <<   4;
v |= ( p[ i ] & 0xF0000000 ) >>  28;

-----
(1) a1 = p[i]
(2) i  = i + 1 (i++)       after (1)      
(4) t3 = a1 & 0xFF         after (1)
(6) t5 = t3 << 4           after (4)
(A) v = t5                 after (6)
------ Sequence Point
(3) a2 = p[i]
(5) t4 = a2 & 0xF0000000   after (3)
(7) t6 = t4 >> 28          after (5)
(8) t7 = v | t6            after (7)
(9) v  = t7                after (8)

Здесь все хорошо определено, поскольку запись в i есть подать в суд на месте и не перечитывать в том же выражении.

Простое правило. не используйте операторы ++ или - внутри большего выражения. но я бы избегал использовать эти знания, поскольку большинство людей не очень хорошо знакомы с работой компилятора. Если вы пишете код, использующий трюки с точками последовательности, кто-то может повторно разложить код, чтобы он выглядел более читабельным, и теперь ваш трюк превратился в неопределенное поведение.

short v = ( p[ i++ ] & 0xFF) <<   4;
v |= ( p[ i ] & 0xF0000000 ) >>  28;

-----
(1) a1 = p[i]
(2) i  = i + 1 (i++)       after (1)      
(4) t3 = a1 & 0xFF         after (1)
(6) t5 = t3 << 4           after (4)
(A) v = t5                 after (6)
------ Sequence Point
(3) a2 = p[i]
(5) t4 = a2 & 0xF0000000   after (3)
(7) t6 = t4 >> 28          after (5)
(8) t7 = v | t6            after (7)
(9) v  = t7                after (8)

Здесь все хорошо определено, поскольку запись в i есть подать в суд на месте и не перечитывать в том же выражении.

Простое правило. не используйте операторы ++ или - внутри более крупного выражения. Ваш код выглядит так же легко читаемым, как этот:

++i; // prefer pre-increment (it makes no difference here, but is a useful habit)
v = ( ( p[ i ] & 0xFF ) << 4 | ( p[ i ] & 0xF0000000 ) >> 28;

См. Эту статью для подробного объяснения порядка оценки:
Каковы все общие неопределенные поведения, о которых программист на C ++ должен знать?

13
ответ дан 8 December 2019 в 02:22
поделиться

Ваше выражение имеет неопределенное поведение, см., Например, this о точках последовательности в операторах C и C ++.

1
ответ дан 8 December 2019 в 02:22
поделиться

Первый пример неопределенное поведение. Вы не можете прочитать переменную более одного раза в выражении, которое также изменяет значение переменной. См. это (среди других мест в Интернете).

9
ответ дан 8 December 2019 в 02:22
поделиться

i увеличивается за некоторое время до следующей точки последовательности. Единственная точка последовательности в выражении, которое вы указали, находится в конце оператора, поэтому в данном случае ответом будет «где-то до конца оператора».

Вот почему вам не следует одновременно изменять lvalue и читать его значение без промежуточной точки последовательности - результат неопределен.

&&, ||, запятая и? операторы вводят точки последовательности, а также конец выражения и вызов функции (последнее означает, что если вы выполните f (i ++, & i), тело f () увидит обновленное значение, если оно использует указатель для проверки i).

13
ответ дан 8 December 2019 в 02:22
поделиться

Иногда до конца выражения.

Не определено чтение объекта, который также был изменен для чего-то еще, кроме определения нового значения, так как не определено, чтобы записывать объект дважды. И вы даже можете получить непоследовательное значение (т.е. чтение чего-то, что не является ни старым, ни новым значением).

3
ответ дан 8 December 2019 в 02:22
поделиться