Почему выражение вместо константы в C for-loop условно?

Во многих соревнованиях по программированию я видел, как люди пишут этот тип for -loop

for(i = 0; i < (1 << 7); i++)

Если я что-то не хватает, это то же самое, что

for(i = 0; i < 128; i++)

Зачем использовать версию (1 << 7)?
Не вычисляет условие каждый раз, когда ненужные служебные данные?

Ответ 1

Да, они эквивалентны поведению.

Тогда почему люди используют версию (1 < 7)?

Я думаю, они используют его для документирования, это сила 2.

Вычисление условия каждый раз должно быть накладными расходами! Я не могу найти причину этого!

На самом деле никакой обычный компилятор не заменит 1 << 7 на 128, и поэтому обе петли будут иметь одинаковые характеристики.

(C11, 6.6p2) "Постоянное выражение может быть оценено во время трансляции, а не во время выполнения, и, соответственно, может использоваться в любом месте, где может быть константа".

Ответ 2

Переведите каждый из этих вариантов на простой английский:

for(i = 0; i < (1 << 7); i++) // For every possible combination of 7 bits
for(i = 0; i < 128; i++)      // For every number between 0 and 127

В обоих случаях поведение выполнения должно быть одинаковым.

Фактически, предполагая достойный компилятор, даже код сборки должен быть идентичным.

Итак, первый вариант по существу используется только для того, чтобы "сделать выражение".

Вы можете просто использовать второй вариант и добавить комментарий выше.

Ответ 3

1 << 7 является постоянным выражением, компилятор рассматривает его как 128, нет накладных расходов во время выполнения.

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

Ответ 4

Тогда почему люди используют версию (1 < 7)?

Это форма документации, это не волшебное число, а 2^7 (от двух до седьмой степени), что имеет смысл для того, кто написал код. Современный оптимизирующий компилятор должен генерировать тот же самый код для обоих примеров, поэтому нет необходимости использовать эту форму, и есть преимущество добавления контекста.

Используя godbolt, мы можем убедиться, что это действительно так, по крайней мере для нескольких версий gcc, clang и icc. Используя простой пример с побочными эффектами, чтобы убедиться, что код не полностью оптимизирован:

#include <stdio.h>

void forLoopShift()
{
  for(int i = 0; i < (1 << 7); i++)
  {
    printf("%d ", i ) ;
  }
}

void forLoopNoShift()
{
  for(int i = 0; i < 128; i++)
  {
        printf("%d ", i ) ;
  }
}

Для соответствующей части кода мы видим, что они генерируют следующее видеть его в прямом эфире:

cmpl    $128, %ebx

То, что мы имеем, представляет собой целочисленное константное выражение, определенное в черновом стандартном разделе C11 6.6 Константные выражения, которые гласят:

Целочисленное константное выражение117) должно иметь целочисленный тип и иметь только операнды которые являются целыми константами, константами перечисления, символьными константами, sizeof выражения, результаты которых являются целыми константами, [...]

и

Константные выражения не должны содержать назначение, приращение, декремент, вызов функции, или запятыми, за исключением случаев, когда они содержатся в подвыражении, которое не является evaluated.115)

и мы можем видеть, что постоянное выражение разрешено оценивать во время перевода:

Постоянное выражение может быть оценено во время трансляции, а не во время выполнения, и соответственно, может использоваться в любом месте, где константа может быть.

Ответ 5

для (i = 0; я < (1 < 7); я ++)

и

для (i = 0; я < 128; я ++)

дает такую ​​же производительность, но разработчик может воспользоваться огромным преимуществом в случае (i = 0; я < (1 < 7); я ++) используется в цикле как

for(int k = 0; k < 8; k++)
{
  for(int i = 0; i < (1 << k); i++)
   {
    //your code
    }

}

Теперь он находится во внутреннем верхнем пределе цикла, то есть (1 < k) изменяется с мощностью 2 времени выполнения. Но это применимо, если ваш алгоритм требует этой логики.

Ответ 6

Компилятор выводит один и тот же код для обоих случаев. вы, вероятно, захотите использовать разные формы в зависимости от контекста.

  • Вы можете использовать NUM_STEPS или NUM_ELEMENTS_IN_NETWORK_PACKET, когда это постоянная часть или выбор дизайна в вашем алгоритме, который вы хотите прояснить.
  • Или вы можете написать 128, чтобы очистить его 128, константу.
  • Или напишите 1 << 7, если вы на конкурсе, и тест сказал что-то вроде "запустите его 2 ^ 7 раз".

Или вы можете показать, что знаете бит-операции!

По моему скромному мнению, программирование похоже на письмо для двух человек, компилятора и человека, который должен будет его прочитать. То, что вы имеете в виду, должно быть понятно для обоих.

Ответ 7

Он оценивается препроцессором, поскольку оба операнда являются постоянными.

Но если вы собираетесь использовать номер вместо сдвига бит, не должно быть 0x0100?