Відступ #defines


99

Я знаю, що #defines і т.д. зазвичай ніколи не відступають. Чому?

Я зараз працюю над деяким кодом, який містить жахливу суміш #defines, #ifdefs, #elses, #endifs тощо. Все це часто змішується з нормальним кодом C. Невідступ #defines робить їх важкими для читання. І суміш відступного коду з невідступними #defines - це кошмар.

Яка перевага відступу без відступу #define? Це робить мене поганою людиною, якщо я відступаю від них? Хіба це не дуже приємно?

#ifdef SDCC
    #if DEBUGGING == 1
        #if defined (pic18f2480)
            #define FLASH_MEMORY_END 0x3DC0
        #elif defined (pic18f2580)
            #define FLASH_MEMORY_END 0x7DC0
        #else
            #error "Can't set  up flash memory end!"
        #endif
    #else
        #if defined (pic18f2480)
            #define FLASH_MEMORY_END 0x4000
        #elif defined (pic18f2580)
            #define FLASH_MEMORY_END 0x8000
        #else
            #error "Can't set  up flash memory end!"
        #endif
    #endif
#else
    #if DEBUGGING == 1
        #define FLASH_MEMORY_END 0x7DC0
    #else
        #define FLASH_MEMORY_END 0x8000
    #endif
#endif

Відповіді:


103

Препроцесор Pre-ANSI C не давав місця між початком рядка та символом "#"; провідний "#" завжди повинен був бути розміщений у першій колонці.

Компіляторів до ANSI C в наші дні немає. Використовуйте будь-який стиль (пробіл перед "#" або пробіл між "#" та ідентифікатором), який ви бажаєте.

http://www.delorie.com/gnu/docs/gcc/cpp_48.html


26

Як уже було сказано, деякі компілятори Pre-ANSI вимагали, щоб # було першим знаком у рядку, але їм не потрібно було додавати директиву про препроцесор, тому відступ було зроблено таким чином.

#ifdef SDCC
#  if DEBUGGING == 1
#    if defined (pic18f2480)
#      define FLASH_MEMORY_END 0x3DC0
#    elif defined (pic18f2580)
#      define FLASH_MEMORY_END 0x7DC0
#    else
#      error "Can't set  up flash memory end!"
#    endif
#  else
#    if defined (pic18f2480)
#      define FLASH_MEMORY_END 0x4000
#    elif defined (pic18f2580)
#      define FLASH_MEMORY_END 0x8000
#    else
#      error "Can't set  up flash memory end!"
#    endif
#  endif
#else
#  if DEBUGGING == 1
#    define FLASH_MEMORY_END 0x7DC0
#  else
#    define FLASH_MEMORY_END 0x8000
#  endif
#endif

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


16

Що стосується розбору директив препроцесора, стандарт C99 (і стандарт C89 перед ним) були чіткими щодо послідовності операцій, які логічно виконував компілятор. Зокрема, я вважаю, що це означає, що цей код:

/* */ # /* */ include /* */ <stdio.h> /* */

еквівалентно:

#include <stdio.h>

На краще чи гірше, GCC 3.4.4 з '-std = c89 -pedantic' приймає лінію навантаження на коментар, у будь-якому випадку. Я не виступаю за це як стиль - ні на секунду (це примарно). Я просто думаю, що це можливо.

Розділ 5.1.1.2 ISO / IEC 9899: 1999, етапи перекладу:

  1. [Зображення символів, включаючи триграфи]

  2. [Спланування ліній - видалення зворотної косої лінії нового рядка]

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

  4. Попередні директиви виконуються, макровикликання розгортаються, [...]

Розділ 6.10 Директиви щодо попередньої обробки:

Директива щодо попередньої обробки складається з послідовності символів попередньої обробки, що починається з маркера # попередньої обробки, який (на початку 4 етапу перекладу) або є першим символом у вихідному файлі (необов'язково після пробілу, що не містить символів нового рядка) або того слід пробіл, що містить щонайменше один символ нового рядка, і закінчується наступним символом нового рядка.

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

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

Примітно, що видалення зворотної косої лінії - нового рядка відбувається перед аналізом коментарів. Отже, не слід закінчувати //коментарі зворотною косою рисою.


7

Я не знаю, чому це не так часто. Безумовно, бувають випадки, коли мені подобається вводити директиви щодо препроцесорів.

Одне, що постійно перешкоджає (а іноді і переконує мене припинити спроби) - це те, що багато чи більшість редакторів / IDE будуть кидати директиву в колонку 1 при найменшій провокації. Що дратує як пекло.


5

Сьогодні я вважаю, що це головним чином вибір стилю. Я думаю, що в один момент у далекому минулому не всі компілятори підтримали поняття визначання препроцесора. Я провів деякі дослідження і не зміг підкріпити це твердження. Але в будь-якому випадку, виявляється, що всі сучасні компілятори підтримують ідею відступу макросу попереднього процесора. У мене немає копії стандарту C або C ++, хоча я не знаю, це стандартна поведінка чи ні.

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

#if COND1
void foo() {
  #if COND2
  int i;
    #if COND3
  i = someFunction()
  cout << i << eol;
    #endif
  #endif
}
#endif

14
Причина цього коду виглядає дивно, тому що ви створили два "потоки" відступу. Я б відступив рядок 4 ще одним рівнем, а я б відступив рядки 6 та 7 ще двома рівнями.
Кевін Лаїт

3
Повністю згоден. Я іноді навіть ставлю брекети, щоб # if виглядав так само, як "if".
baash05

3
Я дуже намагаюся впорядкувати свій код так, щоб він не мав #ifdef рядків у частинах, де у мене є фактичний код. Натомість, якщо мені потрібні умовні речі, я або розміщую їх у факторних функціях, або фактурую макроси; набагато зрозуміліше, як я знаходжу (ну, принаймні, це мені). В ідеалі всі деталі, які є факторними, будуть знаходитись в інших файлах (заголовки або умовно складені вихідні файли; звичайне "умова" - це те, для якої платформи будується код).
Стипендіати Доналу

2
Я б відступив рядки 4 одного рівня, а рядки 6 та 7 - два рівні.
Rocketmagnet

3

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

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

Зберігання директив, візуально відокремлених від решти коду, важливіше, коли вони перемежовуються з кодом (а не виділеним блоком директив, як у наведеному прикладі).


3
З точки зору IP, яка різниця між тим, що не складено, і тим, чого не досягнуто через jmp.
baash05

2

Я зараз працюю над деяким кодом, який містить жахливу суміш #defines, #ifdefs, #elses, #endifs, #etc. Все це часто змішується із звичайним кодом С. Невідступ #defines робить їх важкими для читання. І суміш відступного коду з невідступними #defines - це кошмар.

Поширене рішення - коментувати директиви, щоб ви могли легко знати, на що вони посилаються:

#ifdef FOO
/* a lot of code */
#endif /* FOO */

#ifndef FOO
/* a lot of code */
#endif /* not FOO */

6
Я бачив цей стиль, його використовує мій бос. І, як і решта його коду, це просто створює безлад. Уявіть, що вилучіть усі відступи зі звичайних операторів if () і замість цього скористайтеся цими коментарями. Ви скаржитеся, що не можете легко зрозуміти, на що вони посилаються.
Rocketmagnet

2

Майже у всіх доступних на сьогодні компіляторах C / CPP він не обмежений. Користувач вирішить, як вирівняти код. Тож щасливе кодування.


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