Що таке ідіоматичне використання довільних блоків у С?


15

Блок - це список операторів, які слід виконати. Приклади того, де блоки з’являються в C, є через деякий час і в операторах 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 десять разів. Напевно, я міг бачити ситуації, коли бажано утримувати низьку кількість імен змінних. Можливо, в макророзширенні. Однак я не бачу жодної важкої причини, щоб потреба в цій функції. Чи може хто-небудь допомогти мені зрозуміти використання цієї функції, подавши деякі ідіоми, де вона використовується.


1
Чесно кажучи, я просто намагаюся зрозуміти синтаксис С, і мені цікаво.
Джонатан Галлахер

Дух С - довіряти програмісту. Програміст має в своєму розпорядженні силою зробити щось велике ... або зробити щось жахливе. Особисто мені не подобаються перевантажені імена змінних, але інший програміст може. Зрештою, якщо ми домагаємося мінімальних помилок ... чому ми повинні сперечатися?
Стрічка бітів

1
Це відчуття, яке я отримую від C. Я весь за синтаксис, який підтримує різні стилі кодування (добре, якщо семантика мови нормальна). Просто це ... Я бачив це, і моя негайна відповідь полягала в тому, що я міг застосувати перетворення від джерела до джерела, перейменувавши всі змінні в блок із свіжими іменами, і повністю вирівняти блок. Щоразу, коли я думаю, що міг би чогось позбутися, я припускаю, що я щось пропустив.
Джонатан Галлахер

Смішні терміни, як не-C програміст я натрапив на цей синтаксис сьогодні в коді С і мені було цікаво, для чого це потрібно. Я радий, що ти запитав.
Брендон

Відповіді:


8

Ідея полягає не в тому, щоб кількість імен змінних змінюватись низькою чи іншим чином заохочувати повторне використання імен, а скоріше обмежувати область змінних. Якщо у вас є:

int x = 0;
//...
{
    int y = 3;
    //...
}
//...

то область дії yобмежується блоком, а це означає, що ви можете забути про нього перед або після блоку. Ви бачите, що це використовується найчастіше у зв’язку з петлями та умовними умовами. Ви також часто бачите це на мовах, подібних до С, таких як C ++, де змінна, що виходить за межі, спричиняє її руйнування.


Я думаю, що блок природним чином відбувається з умовними, петлями та функціональними органами. Мені цікаво те, що в С я можу розмістити блок де завгодно. Ваш другий коментар про C ++ цікавий - те, що залишення області приводить до руйнування. Це стосується лише збирання сміття, або це таке інше використання: чи можу я взяти функціональний орган, який має чіткі "секції" та використовувати блоки для управління слідом пам'яті, викликаючи руйнування змінного простору?
Джонатан Галлахер

1
Наскільки мені відомо, C попередньо виділить усю пам'ять для всіх змінних функції. Якщо вони вийдуть з області застосування через функцію, це не матиме переваг щодо продуктивності у C. Так само, коли змінні вводять область "лише іноді", не змінить слід пам'яті під час будь-якого виклику функції
Gankro

@Gankro Це може мати вплив, якщо існує кілька ексклюзивних вкладених областей. Компілятор може повторно використовувати унікальний заздалегідь розміщений фрагмент пам'яті для змінних у кожному з цих областей. Звичайно, причина, чому це не приходить до тями, полягає в тому, що якщо один довільний діапазон вже є ознакою, яку ви, мабуть, потрібно витягти до функції, два або більше довільних діапазонів, безумовно, є хорошим показником того, що вам потрібно зробити рефактор. І все-таки воно з'являється як розумне рішення у таких речах, як блоки перемикання корпусів час від часу.
тне

16

Тому що за старих днів C нових змінних можна було оголосити лише в новому блоці.

Таким чином програмісти можуть вводити нові змінні в середину функції, не протікаючи її і мінімізуючи використання стеку.

З сучасними оптимізаторами це марно і знак того, що вам потрібно розглянути можливість вилучення блоку у власній функції.

У операторі перемикання корисно вкладати справи у власні блоки, щоб уникнути подвійного оголошення.

У C ++ це дуже корисно, наприклад, для охоронців блоку RAII та забезпечення деструкторів запускати фіксатор випуску, коли виконання виходить за рамки та все ще робить інші речі поза критичним розділом.


2
+1 для охоронців замків RAII. Незважаючи на це, чи не може ця сама концепція бути корисною для розгортання буфера великих стеків у частинах програми? Я ніколи цього не робив, але звучить як щось, що напевно могло б виникнути у вбудованому коді ...
J Trana

2

Я б не розглядав це як "довільні" блоки. Це не така функція, яка призначена настільки для використання розробниками, але те, як C використовує блоки, дозволяє використовувати ту саму конструкцію блоку в ряді місць з однаковою семантикою. Блок (на C) - це нова область, і змінні, що залишають його, усуваються. Це рівномірно незалежно від способу використання блоку.

В інших мовах це не так. Це має перевагу в тому, що дозволяє менше зловживань, як ви показуєте, але недолік, який блокують, поводиться по-різному в залежності від того, в якому контексті вони перебувають.

Я рідко бачив окремі блоки, що використовуються в C або C ++ - часто, коли є велика структура або об'єкт, який представляє з'єднання або щось, що ви хочете примусити знищити. Зазвичай це натяк на те, що ваша функція робить занадто багато речей і / або занадто довга.


1
На жаль, я регулярно бачу окремі блоки - майже у всіх випадках, тому що функція працює дуже багато та / або занадто довго.
mattnz

Я також регулярно бачу і пишу окремі блоки в C ++, але виключно для того, щоб змусити знищити обгортки навколо замків та інших спільних ресурсів.
J Trana

2

Ви повинні усвідомити, що принципи програмування, які зараз здаються очевидними, не завжди були такими. Передовий досвід C значною мірою залежить від того, скільки вам років. Коли C вперше був введений, розбиття коду на невеликі функції вважалося занадто неефективним. Денніс Річі в основному збрехав і сказав, що виклики функцій були дійсно ефективними в C (їх тоді не було), через що люди почали їх більше використовувати, хоча програмісти на C-то чомусь ніколи не пройшли повну культуру передчасної оптимізації.

це є хорошою практикою програмування навіть сьогодні , щоб обмежити область дії змінних, як можна менше . Сьогодні ми зазвичай це робимо, створюючи нову функцію, але якщо функції вважаються дорогими, введення нового блоку є логічним способом обмежити сферу дії без накладних витрат виклику функції.

Однак я почав програмувати на C понад 20 років тому, тоді, коли вам довелося оголосити всі свої змінні у верхній частині області, і я не пригадую, щоб затінення змінних, як це колись, вважалося хорошим стилем. Повторне позначення в двох блоках один за одним, як у заяві перемикача, так, але не затінення. Можливо, якщо змінна вже була використана, а конкретне ім’я було дуже ідіоматичним для API, який ви викликаєте, наприклад, як destі srcв strcpy, наприклад.


1

Довільні блоки корисні для введення змінних посередників, які використовуються лише в особливих випадках обчислення.

Це загальна закономірність наукових обчислень, де числові процедури зазвичай:

  1. покладатися на безліч параметрів або посередницьких величин;
  2. доведеться мати справу з безліччю особливих справ.

З-за другого пункту корисно вводити тимчасові змінні з обмеженою сферою дії, що досягається більш сильно, використовуючи довільний блок або вводячи допоміжну функцію.

Хоча введення допоміжної функції може виглядати як нікому не відоме або найкраща практика сліпого слідування, насправді мало користі для цього в цій конкретній ситуації.

Оскільки існує велика кількість параметрів і посередницьких величин, ми хочемо ввести структуру для передачі їх до допоміжної функції.

Але, оскільки ми хочемо бути наслідком своєї практики, ми не будемо вводити лише одну допоміжну функцію, а декілька. Отже, чим раніше ми вводимо спеціальні структури, що передають параметри для кожної функції, які вводять багато накладних кодів для переміщення параметрів вперед і назад, або ми вводимо одну, яка буде керувати ними всіма структурою робочого листа, яка містить усі наші змінні, але виглядає як грайпак бітів без консистенції, де в будь-який час лише половина параметрів має цікаве значення.

Тому ці допоміжні структури, як правило, громіздкі, а їх використання означає вибір між кодовим розшаруванням або введення абстракції, сфера застосування якої занадто широка і послаблює значення програми, а не посилювати її .

Введення допоміжних функцій може полегшити одиничне тестування програми, запровадивши більш точну деталізацію тесту, але поєднуючи тестування одиниць для не тих процедур низького рівня та тестування регресії у вигляді порівнянь (з нумером) числових слідів процедур, є однаково хорошою роботою .


0

Взагалі повторне використання імен змінних таким чином викликає занадто велику плутанину у майбутніх читачів вашого коду. Краще просто назвати внутрішню змінну чимось іншим. Насправді мова C # спеціально забороняє використання змінних таким чином.

Я міг бачити, як використання вкладених блоків у розширеннях макросів буде корисним для запобігання зіткнень змінних імен.


0

Це для визначення речей, які зазвичай не мають сфери застосування на цьому рівні. Це вкрай рідко, і в цілому рефакторинг - кращий вибір, але я переживав це один-два рази в минулому в операторах переключення:

switch(foo) {
   case 1:
      {
         // bar
      }
   case 2:
   case 3:
      // baz
      break;
   case 4:
   case 5:
      // bang
      break;
}

Якщо ви розглядаєте технічне обслуговування, то їх рефакторинг повинен бути врівноваженим із виконанням всіх реалізацій підряд, доки кожна з них становить лише пару рядків. Зробити їх разом може бути простіше, ніж список імен функцій.

У моєму випадку, якщо я добре пригадую, більшість випадків були незначними варіаціями одного і того ж коду - за винятком того, що один з них був ідентичним іншому, лише з додатковою попередньою обробкою. Перемикач виявився найпростішою формою для прийняття коду, а додатковий обсяг дозволив використовувати додаткові локальні змінні, про які він і в подальших випадках не потрібно було турбуватися (наприклад, імена змінних, випадково перекриваючись одна з визначених перед комутатором, але використаний пізніше).

Зауважте, що оператор перемикання - це просто його використання. Я впевнений, що це може застосовуватися і в інших місцях, але я не пригадую, щоб їх ефективно використовували таким чином.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.