113

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

我目前正在编写一些代码,其中包含#defines、#ifdefs、#elses、#endifs 等的可怕混合。所有这些通常与普通的 C 代码混合在一起。s的非缩进#define使它们难以阅读。缩进代码与非缩进#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
4

9 回答 9

116

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

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

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

于 2009-04-25T15:36:15.763 回答
28

正如一些人已经说过的那样,一些 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 没有以有用的方式为序列着色。

于 2010-07-03T08:47:42.160 回答
19

关于预处理器指令的解析,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. 源文件被分解为预处理标记和空白字符序列(包括注释)。源文件不应以部分预处理标记或部分注释结尾。每个注释被一个空格字符替换。保留换行符。除换行符之外的每个非空空白字符序列是保留还是替换为一个空格字符是实现定义的。

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

第 6.10 节预处理指令说:

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

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

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

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

于 2009-04-25T16:14:13.300 回答
7

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

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

于 2009-04-25T15:56:31.753 回答
5

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

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

#if COND1
void foo() {
  #if COND2
  int i;
    #if COND3
  i = someFunction()
  cout << i << eol;
    #endif
  #endif
}
#endif
于 2009-04-25T15:19:10.530 回答
3

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

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

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

于 2009-04-25T15:21:18.000 回答
3

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

于 2019-11-21T15:23:53.887 回答
2

我目前正在编写一些代码,其中包含#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 */
于 2009-04-25T15:17:30.940 回答
0

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

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

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

于 2020-10-25T08:00:33.270 回答