Я должен был найти тот же ответ. Лучший пример, который я нашел, http://www.cssplay.co.uk/menu/tablescroll.html - я нашел, что пример № 2 работал хорошо на меня. Необходимо будет установить высоту внутренней таблицы со Сценарием Java, остальное - CSS.
Проблема заключается в порядке оценки:
Стандарт 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 ++ должен знать?
Ваше выражение имеет неопределенное поведение, см., Например, this о точках последовательности в операторах C и C ++.
Первый пример неопределенное поведение. Вы не можете прочитать переменную более одного раза в выражении, которое также изменяет значение переменной. См. это (среди других мест в Интернете).
i увеличивается за некоторое время до следующей точки последовательности. Единственная точка последовательности в выражении, которое вы указали, находится в конце оператора, поэтому в данном случае ответом будет «где-то до конца оператора».
Вот почему вам не следует одновременно изменять lvalue и читать его значение без промежуточной точки последовательности - результат неопределен.
&&, ||, запятая и? операторы вводят точки последовательности, а также конец выражения и вызов функции (последнее означает, что если вы выполните f (i ++, & i), тело f () увидит обновленное значение, если оно использует указатель для проверки i).
Иногда до конца выражения.
Не определено чтение объекта, который также был изменен для чего-то еще, кроме определения нового значения, так как не определено, чтобы записывать объект дважды. И вы даже можете получить непоследовательное значение (т.е. чтение чего-то, что не является ни старым, ни новым значением).