Блок - это список операторов, которые должны быть выполнены. Примеры того, где блоки появляются в C, - через оператор while и оператор if
while( boolean expression)
statement OR block
if (boolean expression)
statement OR block
C также позволяет блоку быть вложенным в блок. Я могу использовать это, чтобы повторно использовать имена переменных, предположим, что мне действительно нравится 'x'
int x = 0;
while (x < 10)
{
{
int x = 5;
printf("%d",x)
}
x = x+1;
}
напечатает номер 5 десять раз. Я думаю, я мог видеть ситуации, когда желательно, чтобы количество имен переменных было низким. Возможно в расширении макроса. Тем не менее, я не вижу какой-либо веской причины для необходимости этой функции. Может ли кто-нибудь помочь мне понять использование этой функции, предоставив некоторые идиомы, где она используется.
Ответы:
Идея состоит не в том, чтобы поддерживать малое количество имен переменных или иным образом поощрять повторное использование имен, а в том, чтобы ограничить область видимости переменных. Если у вас есть:
тогда область действия
y
ограничена блоком, что означает, что вы можете забыть об этом до или после блока. Вы видите, что это чаще всего используется в связи с циклами и условными выражениями. Вы также видите это чаще в C-подобных языках, таких как C ++, где переменная, выходящая из области видимости, вызывает ее разрушение.источник
Потому что в старые времена C новые переменные могли быть объявлены только в новом блоке.
Таким образом, программисты могут вводить новые переменные в середине функции, не пропуская ее и сводя к минимуму использование стека.
С сегодняшними оптимизаторами это бесполезно и признак того, что вам нужно рассмотреть возможность извлечения блока в его собственной функции.
В операторе switch полезно заключать регистры в свои собственные блоки, чтобы избежать двойного объявления.
В C ++ это очень полезно, например, для защитников блокировки RAII и обеспечения того, чтобы деструкторы запускали блокировку освобождения, когда выполнение выходит из области видимости, и все еще выполняли другие действия вне критической секции.
источник
Я не смотрю на это как на «произвольные» блоки. Это не особенность, предназначенная для разработчиков, но способ, которым C использует блоки, позволяет использовать одну и ту же конструкцию блока во многих местах с одинаковой семантикой. Блок (в C) - это новая область, и переменные, которые покидают его, исключаются. Это единообразно независимо от того, как используется блок.
На других языках это не так. Это имеет то преимущество, что позволяет меньше злоупотреблять, как вы показываете, но недостатком является то, что блоки ведут себя по-разному в зависимости от того, в каком контексте они находятся.
Я редко видел отдельные блоки, используемые в C или C ++ - часто, когда есть большая структура или объект, представляющий соединение или что-то, что вы хотите принудительно уничтожить. Обычно это намек на то, что ваша функция делает слишком много вещей и / или слишком длинна.
источник
Вы должны понимать, что принципы программирования, которые кажутся очевидными сейчас, не всегда были такими. C передовой практикой во многом зависит от того, сколько лет вашим примерам. Когда C был впервые представлен, разбивать ваш код на маленькие функции считалось слишком неэффективным. Деннис Ритчи в основном солгал и сказал, что вызовы функций были действительно эффективны в C (они не были в то время), и именно это заставило людей больше их использовать, хотя программисты на C так или иначе так и не смогли полностью преодолеть культуру преждевременной оптимизации.
Это является хорошей практикой программирования даже сегодня , чтобы ограничить область действия переменных, как можно меньше . В настоящее время мы обычно делаем это, создавая новую функцию, но если функции считаются дорогими, введение нового блока является логичным способом ограничения вашей области действия без затрат на вызов функции.
Тем не менее, я начал программировать на C более 20 лет назад, когда вам приходилось объявлять все переменные в верхней части области, и я не помню, чтобы теневое копирование переменных считалось хорошим стилем. Перераспределение в два блока один за другим, как в операторе switch, да, но не дублирование. Возможно, если переменная уже использовалась, и конкретное имя было очень идиоматичным для API, который вы вызываете, например,
dest
иsrc
вstrcpy
, например.источник
Произвольные блоки полезны для введения промежуточных переменных, которые используются только в особых случаях вычислений.
Это распространенная модель в научных вычислениях, где числовые процедуры обычно:
В связи со вторым моментом полезно ввести временные переменные ограниченной области действия, что достигается путем использования произвольного блока или введения вспомогательной функции.
Хотя введение вспомогательной функции может показаться легким делом или лучшей практикой слепого следования, на самом деле в этой конкретной ситуации это мало что дает .
Поскольку существует множество параметров и промежуточных величин, мы хотим ввести структуру для передачи их вспомогательной функции.
Но, поскольку мы хотим следовать нашим практикам, мы не будем вводить только одну вспомогательную функцию, но несколько. Итак, мы вводим специальные структуры, передающие параметры для каждой функции, которые вводят много накладных расходов на код для перемещения параметров вперед или назад, или мы вводим единую структуру, которая будет управлять всеми их структурами листа, которая содержит все наши переменные, но выглядит как пакет битов без консистенции, где в любое время только половина параметров имеет интересный смысл.
Поэтому эти вспомогательные структуры обычно громоздки, и их использование означает выбор между раздуванием кода или введением абстракции, область действия которой слишком широка и ослабляет смысл программы, вместо того, чтобы усиливать ее .
Введение вспомогательных функций может упростить модульное тестирование программы, введя более тонкую гранулярность теста, но комбинируя модульное тестирование не для того, чтобы низкоуровневые процедуры и регрессионное тестирование в форме сравнения (с numdiff) числовых следов процедур выполняли одинаково хорошую работу ,
источник
Как правило, повторное использование имен переменных вызывает слишком много путаницы для будущих читателей вашего кода. Лучше просто назвать внутреннюю переменную как-нибудь еще. Фактически, язык C # специально запрещает использование переменных таким способом.
Я мог видеть, как использование вложенных блоков в расширениях макросов было бы полезно для предотвращения конфликтов имен переменных.
источник
Это для областей видимости, которые обычно не имеют возможности на этом уровне. Это крайне редко, и в целом рефакторинг - лучший выбор, но в прошлом я сталкивался с ним один или два раза в выражениях switch:
Когда вы рассматриваете обслуживание, их рефакторинг должен быть сбалансирован с наличием всех реализаций подряд, если каждая из них имеет длину всего несколько строк. Может быть проще иметь их все вместе, а не список имен функций.
В моем случае, если я правильно помню, в большинстве случаев были небольшие вариации одного и того же кода - за исключением того, что один из них был идентичен другому, только с дополнительной предварительной обработкой. Переключатель оказался самой простой формой для кода, и дополнительная область позволила использовать дополнительные локальные переменные, о которых ему и в других случаях не нужно было беспокоиться (например, имена переменных, случайно перекрывающиеся с именем, определенным до переключения, но использовал позже).
Обратите внимание, что оператор switch - это просто использование его, с которым я столкнулся. Я уверен, что это может применяться и в других местах, но я не припоминаю, чтобы они эффективно использовались таким образом.
источник