Отступы #defines - PullRequest
       46

Отступы #defines

92 голосов
/ 25 апреля 2009

Я знаю, что #define s и т. Д. Обычно никогда не имеют отступов. Почему?

Я сейчас работаю над некоторым кодом, который имеет ужасную смесь #define s, #ifdef s, #else s, #endif s и т. Д. Все это часто смешивается с обычным кодом C , Отсутствие отступа #define делает их трудными для чтения. И смесь кода с отступом и кода без отступа #define s - это кошмар.

В чем преимущество отсутствия отступа #define s? Это делает меня плохим человеком, если я сделаю отступ? Разве это не намного лучше?

#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

Ответы [ 7 ]

96 голосов
/ 25 апреля 2009

Pre-ANSI C препроцессор не допускал пробела между началом строки и символом "#"; начальный "#" должен был всегда находиться в первом столбце.

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

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

24 голосов
/ 03 июля 2010

Как уже говорили некоторые, некоторые пре-ANSI-компиляторы требовали, чтобы символ # был первым символом в строке, но им не требовалось, чтобы к нему была присоединена директива de preprocessor, поэтому отступ был сделан таким образом.

#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 голосов
/ 25 апреля 2009

Что касается синтаксического анализа директив препроцессора, стандарт C99 (и стандарт C89 до него) были ясны относительно последовательности операций, выполняемых компилятором логически. В частности, я считаю, что это означает, что этот код:

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

эквивалентно:

#include <stdio.h>

Что бы там ни было, GCC 3.4.4 с '-std = c89 -pedantic' принимает строку с комментариями, во всяком случае. Я не защищаю это как стиль - ни на секунду (это ужасно). Я просто думаю, что это возможно.

ISO / IEC 9899: 1999, раздел 5.1.1.2. Этапы перевода гласят:

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

  2. [Объединение строк - удаление новой строки с обратной косой чертой]

  3. Исходный файл разлагается на токены предварительной обработки и последовательности символы пробела (включая комментарии). Исходный файл не должен заканчиваться частичный токен предварительной обработки или частичный комментарий. Каждый комментарий заменяется один пробел Символы новой строки сохраняются. Будь каждый непустой последовательность символов пробела, кроме новой строки, сохраняется или заменяется один пробел определяется реализацией.

  4. Выполняются директивы предварительной обработки, расширяются вызовы макросов, [...]

Раздел 6.10 Директивы предварительной обработки гласит:

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

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

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

Примечательно, что удаление обратной косой черты происходит перед анализом комментариев. Следовательно, вы не должны заканчивать // комментарии обратной косой чертой.

6 голосов
/ 25 апреля 2009

Не знаю, почему это не так часто встречается. Конечно, бывают моменты, когда мне нравится вставлять директивы препроцессора.

Одна вещь, которая мешает мне (и иногда убеждает меня прекратить попытки), это то, что многие или большинство редакторов / IDE бросают директиву в столбец 1 при малейшей провокации. Что чертовски раздражает.

5 голосов
/ 25 апреля 2009

В наши дни я считаю, что это в основном выбор стиля. Я думаю в какой-то момент в далеком прошлом не все компиляторы поддерживали идею определения отступа препроцессора. Я провел некоторое исследование и не смог подтвердить это утверждение. Но в любом случае кажется, что все современные компиляторы поддерживают идею отступа макроса препроцессора. У меня нет копии стандарта C или C ++, хотя я не знаю, стандартное ли это поведение или нет.

Что касается того, хороший это стиль или нет. Лично мне нравится идея держать их всех слева. Это дает вам постоянное место для их поиска. Да, это может раздражать, когда есть очень вложенные макросы. Но если вы сделаете отступ, вы в конечном итоге получите еще более странный код.

#if COND1
void foo() {
  #if COND2
  int i;
    #if COND3
  i = someFunction()
  cout << i << eol;
    #endif
  #endif
}
#endif
3 голосов
/ 25 апреля 2009

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

Лично я думаю, что большую часть времени полезно сохранять их без отступов, потому что эти директивы работают отдельно от остальной части вашего кода. Директивы, такие как #ifdef, обрабатываются препроцессором до того, как компилятор увидит ваш код, поэтому блок кода после директивы #ifdef может даже не быть скомпилирован .

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

2 голосов
/ 25 апреля 2009

Сейчас я работаю над некоторым кодом, в котором есть ужасная смесь #defines, #ifdefs, #elses, #endifs, #etc. Все это часто смешивается с нормальным C-кодом. Отсутствие отступа в #defines затрудняет их чтение. И смесь кода с отступом с #defines без отступа - это кошмар.

Распространенным решением является комментирование директив, чтобы вы легко знали, к чему они относятся:

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

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