【问题标题】:Indenting #defines缩进#defines
【发布时间】:2010-10-21 19:18:00
【问题描述】:

我知道#defines 等通常从不缩进。为什么?

我目前正在编写一些代码,其中包含#defines、#ifdefs、#elses、#endifs 等的可怕混合。所有这些通常与普通 C 代码混合. #defines 的非缩进使它们难以阅读。缩进代码与非缩进 #defines 的混合是一场噩梦。

为什么#defines 通常不缩进?是否有不缩进的原因(例如下面的代码)?

#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

【问题讨论】:

    标签: c c-preprocessor indentation code-formatting


    【解决方案1】:

    Pre-ANSI C 预处理器不允许在行首和“#”字符之间留有空格;前导“#”必须始终放在第一列。

    现在不存在 Pre-ANSI C 编译器。使用您喜欢的样式(“#”之前的空格或“#”和标识符之间的空格)。

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

    【讨论】:

      【解决方案2】:

      正如一些人已经说过的,一些 Pre-ANSI 编译器要求 # 是该行的第一个字符,但它们不需要附加 de 预处理器指令,因此以这种方式进行缩进。

      #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 没有以有用的方式为序列着色。

      【讨论】:

        【解决方案3】:

        关于预处理器指令的解析,C99 标准(以及之前的 C89 标准)清楚地说明了编译器在逻辑上执行的操作顺序。特别是,我相信这意味着这段代码:

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

        相当于:

        #include <stdio.h>
        

        无论好坏,带有“-std=c89 -pedantic”的 GCC 3.4.4 无论如何都会接受包含注释的行。我并不是在提倡将其作为一种风格 - 不是一秒钟(这太可怕了)。我只是觉得有可能。

        ISO/IEC 9899:1999 第 5.1.1.2 节翻译阶段说:

        1. [字符映射,包括三元组]

        2. [行拼接-去除反斜线换行]

        3. 源文件被分解为预处理标记和序列 空白字符(包括 cmets)。源文件不应以 部分预处理标记或部分注释。每条评论都替换为 一个空格字符。保留换行符。是否每个非空 除换行符以外的空白字符序列被保留或替换为 一个空格字符是实现定义的。

        4. 执行预处理指令,扩展宏调用,[...]

        第 6.10 节预处理指令说:

        预处理指令由一系列预处理标记组成,以 a # 预处理标记(在翻译阶段 4 开始时)是第一个字符 在源文件中(可选地在不包含换行符的空格之后)或 跟在包含至少一个换行符的空格之后,并由下一个结束 换行符。

        唯一可能的争议是括号表达式“(在翻译阶段 4 开始时)”,这可能意味着哈希之前的 cmets 必须不存在,因为它们直到阶段 4 结束才会被空格替换。

        正如其他人所指出的,预标准 C 预处理器在许多方面的行为并不统一,预处理器指令之前和之中的空格是不同编译器做不同事情的领域之一,包括不识别带有空格的预处理器指令在他们前面。

        值得注意的是,在分析 cmets 之前会删除反斜杠换行符。 因此,您不应以反斜杠结束 // cmets。

        【讨论】:

          【解决方案4】:

          我不知道为什么它不常见。有时我喜欢缩进预处理器指令。

          一件事一直妨碍我(有时说服我停止尝试)是许多或大多数编辑器/IDE 会在最轻微的挑衅下将指令扔到第 1 列。真是烦人啊。

          【讨论】:

            【解决方案5】:

            这些天我相信这主要是一种风格的选择。我认为在遥远的过去,并非所有编译器都支持缩进预处理器定义的概念。我做了一些研究,但无法支持这一说法。但无论如何,似乎所有现代编译器都支持缩进预处理器宏的想法。我没有 C 或 C++ 标准的副本,所以我不知道这是否是标准行为。

            至于风格好不好。就个人而言,我喜欢把它们都放在左边的想法。它为您提供了一个一致的位置来寻找它们。是的,当有非常嵌套的宏时,它会变得很烦人。但是如果你缩进它们,你最终会得到看起来更奇怪的代码。

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

            【讨论】:

            • 这段代码看起来很奇怪的原因是你创建了两个缩进“流”。我会将第 4 行再缩进一级,并将第 6 行和第 7 行再缩进两级。
            • 完全同意。有时我什至会加上大括号,所以#if 看起来就像 if 一样。
            • 我非常努力地安排我的代码,以便在我有实际代码的部分有 no #ifdef 行。相反,如果我需要有条件的东西,我要么把它放在分解出的函数中,要么放在分解出的宏中;我发现这种方式要清楚得多(嗯,至少对我来说是这样)。理想情况下,所有这些被分解的部分都将在其他文件中(头文件或条件编译的源文件;通常的“条件”是代码正在构建的平台)。
            • 我会将第 4 行缩进一层,将第 6 行和第 7 行缩进两层。
            【解决方案6】:

            对于您给出的示例,可能适合使用缩进使其更清晰,因为您有如此复杂的嵌套指令结构。

            我个人认为在大多数情况下保持它们不缩进很有用,因为这些指令与您的其余代码分开运行。诸如#ifdef 之类的指令在编译器看到您的代码之前由预处理器处理,因此#ifdef 指令之后的代码块甚至可能无法编译

            将指令与代码的其余部分在视觉上分开更为重要,因为它们散布在代码中(而不是像您给出的示例中那样专门的指令块)。

            【讨论】:

            • 从IP的角度来看,没有编译的东西和因为jmp而没有达到的东西有什么区别。
            【解决方案7】:

            在几乎所有当前可用的 C/CPP 编译器中,它都不受限制。由用户决定如何对齐代码。 编码愉快。

            【讨论】:

            • 体面的答案。您能否通过添加一些特定的样式指南参考来改进它?
            【解决方案8】:

            我目前正在编写一些代码,其中包含#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 */
            

            【讨论】:

            • 这种风格我见过,我老板用的。而且,就像他的其他代码一样,它只会弄得一团糟。想象一下,从您的普通 if() 语句中删除所有缩进并改用这些 cmets。你会抱怨你不能轻易看到他们指的是什么。
            【解决方案9】:

            我知道这是个老话题,但我浪费了几天时间寻找解决方案。我同意最初的帖子,如果你有很多代码,打算让代码更干净(在我的例子中,我使用指令来启用/禁用详细日志记录)。最后,我找到了适用于 Visual Studio 2017 的解决方案 here

            如果您喜欢缩进 #pragma 表达式,您可以在以下位置启用它:工具 > 选项 > 文本编辑器 > C/C++ > 格式化 > 缩进 > 预处理器指令的位置 > 保留缩进

            剩下的唯一问题是自动代码布局修复了格式 =(

            【讨论】:

              猜你喜欢
              • 2014-12-17
              • 2014-04-29
              • 2011-03-12
              • 2011-07-12
              • 2013-06-28
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2010-09-12
              相关资源
              最近更新 更多