【问题标题】:Is the backslash acceptable in C and C++ #include directives?C 和 C++ #include 指令中是否可以使用反斜杠?
【发布时间】:2011-08-13 00:10:29
【问题描述】:

有两种常用的路径分隔符:Unix 正斜杠和 DOS 反斜杠。 安息吧,经典 Mac 冒号。 如果在 #include 指令中使用,它们在 C++11、C++03 和 C99 的规则下是否相等标准?

【问题讨论】:

  • 路径名是操作系统的实现细节。与您用来避免在 #include 指令中指定目录名称的编译器设置一样。

标签: c++ c include c-preprocessor header-files


【解决方案1】:

C99 说(§6.4.7/3):

如果字符 '、\、"、// 或 /* 出现在 分隔符之间的序列中,则行为未定义。类似地,如果字符 '、\、// 或 /* 出现在 " 分隔符之间的序列中,行为未定义。

(脚注:因此,类似于转义序列的字符序列会导致未定义的行为。)

C++03 说(§2.8/2):

如果字符 ' 或 \,或者字符序列 /* 或 // 出现在 q-char- 序列或 h-char-sequence 中,或者字符 " 出现在 h-char- 中序列,行为未定义。

(脚注:因此,类似于转义序列的字符序列会导致未定义的行为。)

C++11 说(§2.9/2):

在 q-char-sequence 或 h-char-sequence 中,字符 ' 或 \ 或字符序列 /* 或 // 的出现由实现定义的语义有条件地支持,如下所示字符 " 在 h 字符序列中的外观。

(脚注:因此,类似于转义序列的字符序列可能会导致错误,被解释为与转义序列对应的字符,或者具有完全不同的含义,具体取决于实现。)

因此,尽管任何编译器都可能选择在 #include 路径中支持反斜杠,但任何编译器供应商都不太可能不支持正斜杠,并且反斜杠可能会由于形成转义码而使某些实现出错. (编辑:显然 MSVC 以前需要反斜杠。也许 DOS 衍生平台上的其他人也类似。嗯……我能说什么。)

C++11 似乎放宽了规则,但“有条件地支持”并不比“导致未定义的行为”更好。这种变化更多地反映了某些流行编译器的存在,而不是描述了一个可移植的标准。

当然,这些标准中没有任何内容表明存在路径之类的东西。 个文件系统根本没有路径!但是,许多库都采用路径名,包括 POSIX 和 Boost,因此需要一种可移植的方式来引用子目录中的文件是合理的。

【讨论】:

  • 即使从纯粹的需求驱动的角度来看,“有条件支持的行为”和“未定义的行为”会对编译器施加相同的义务(即没有),前者暗示平台在某些东西将具有合理的含义应该支持该含义,即使标准不要求任何特定的实现来尊重任何特定的含义。遗憾的是,该标准没有对许多其他形式的 UB 应用这种处理方式,许多平台在历史上都赋予了有用的含义。
  • @supercat 好点。这实际上是我提议的comprehensive preprocessor spec revision 的指导原则之一。不幸的是,它似乎被困在委员会官僚机构中:( .
  • @Potatoswatter:您如何看待用“有条件支持的行为”替换许多其他形式的 UB,并添加一种代码可以测试支持的方法(或者使用其他较慢的算法或在不可用时拒绝编译)?许多编译器都有命令行开关来定义标准没有的情况下的行为,但目前源代码无法确认这些开关是否设置得当。此外,在某些情况下,在优化构建中实现正确性所需的代码可能会非常低效且不必要地...
  • ...在未优化的情况下(例如,优化器可能会意识到 for (int i=0; i<size; i++) if (ptr1+i==ptr2) return 1; 相当于大多数平台上未优化的编译器会为 if (ptr2>=ptr1 && ptr2<ptr1+i) return 1; 生成的内容,但如果编译器设置可以保证两者与后者等效的形式不需要优化即可正常执行。可能有一些模块可以修剪某些形式的 UB 可以提高性能,但肯定有其他模块定义某些形式的当前 UB 可以允许比取缔它们更明智的代码。
  • @supercat 听起来很合理。您可以将其浮动在官方std-proposals 列表中。如果你想正式介绍它,我建议先通过反射研究组,因为他们推荐功能测试宏。 UB 研究小组的工作效率似乎较低,他们的官僚作风阻碍了我的提议。 2014 年 2 月,他们承诺对其进行审查,但没有见面。 2014 年 11 月,他们见面了,但只是短暂的,并推迟了任何审查,因为它太大了。有点杂乱无章。
【解决方案2】:

正斜杠是正确的方式;预编译器将在每个平台上尽一切努力获取正确的文件。

【讨论】:

  • 遗憾的是,仅在包含...
  • @Xeo 这不依赖于 MSVC,它是 Windows 本身:现代 Windows 接受正斜杠作为路径分隔符; Windows 98 没有(AFAIR)。
  • @Konrad:大多数问题源于 Windows 命令行工具喜欢使用“/”来表示命令行参数,而不是 UNIX 的“-”或“--”。
  • 在标准中绝对 没有任何内容 说强制使用正斜杠,也没有说“预编译器”(我假设您是在谈论这里的编译器)会神奇地把它变成任何必要的东西。几乎整个事情都是由实现定义的。
  • @trojanfoe 是的,继承自 CP/M,在路径存在之前。我碰巧认为在 DOS 2 中使用“\”作为路径分隔符是计算史上最糟糕的决定之一。这个解决了 re 命令行开关的“兼容性问题”是感知和发明的,而不是实际的,因为这仅适用于现存的 .com 程序,这些程序甚至不知道允许指定路径的新 API。在其他重要的操作系统中,与“\ is universal escape”相关的混乱,他们显然试图普遍迁移到这些操作系统,是完全可以预见的。
【解决方案3】:

这取决于您所说的“可接受”。

斜杠可以接受,反斜杠不能接受有两种含义。

如果您正在编写 C99、C++03 或 C1x,则反斜杠是未定义的,而斜杠是合法的,因此从这个意义上说,反斜杠是不可接受的。

但这对大多数人来说无关紧要。如果您正在编写 C++1x,其中有条件地支持反斜杠,并且您正在编码的平台支持它们,那么它们是可以接受的。如果你正在编写定义反斜杠的 C99/C++03/C1x 的“扩展方言”,同样的处理。而且,更重要的是,无论如何,这种“可接受”的概念在大多数情况下都是毫无意义的。没有任何 C/C++ 标准定义斜杠的含义(或反斜杠在有条件支持时的含义)。标头名称以实现定义的方式(句点)映射到源文件。如果您有文件层次结构,并且您正在询问是否使用反斜杠或斜杠在#include 指令中可移植地引用它们,答案是:两者都不是可移植的。如果你想编写真正可移植的代码,就不能使用头文件的层次结构——事实上,可以说,最好的办法是将所有内容都写在一个源文件中,而不是 #include 除了标准头文件之外的任何内容。

然而,在现实世界中,人们往往想要“足够便携”,而不是“严格便携”。 POSIX 标准规定了斜线的含义,甚至在 POSIX 之外,大多数现代平台——包括 Win32(和 Win64)、用于嵌入式和移动平台(如 Symbian 等)的交叉编译器等——以 POSIX 方式处理斜线,至少在C/C++ #include 指令。任何没有的平台,可能都没有办法让您将源代码树放到它上面,处理您的 makefile/等,等等,所以#include 指令将是您最不必担心的。如果这是您所关心的,那么斜杠是可以接受的,但反斜杠不是。

【讨论】:

  • 虽然实现不需要指定他们对反斜杠做了什么(如果有的话),但任何针对需要在文件名中使用反斜杠的平台的 quality 实现都会指定如何处理它们. C 标准没有说明编译器应如何处理跨多个目录的任何项目,而是依赖于各种平台的实现以适合这些平台的方式运行。
【解决方案4】:

Blackslash 是未定义的行为,即使使用斜线也必须小心。 C99 标准规定:

如果字符 '、\、"、// 或 /* 出现在 分隔符,行为是 不明确的。同样,如果 字符 '、\、// 或 /* 出现在 "分隔符之间的序列, 行为未定义。

【讨论】:

  • 顺便说一句,它在 C++0x 标准中不再未定义。
  • @paxdiabolo:对于 C,在下一个标准的当前草案中,这部分似乎没有改变。所以它看起来会在这里停留一段时间。
【解决方案5】:

始终使用正斜杠 - 它们适用于更多平台。反斜杠在技术上会导致 C++03(标准中的 2.8/2)中的未定义行为。

【讨论】:

  • 它们并非适用于所有平台。有些平台没有/ 作为目录分隔符。反斜杠现在是 C++0x 中实现定义的行为,但随后,围绕包含的大多数其他内容也是如此。
【解决方案6】:

标准对#include 的规定是:

搜索一系列实现定义的位置 由指定序列唯一标识的标头 分隔符,并导致该指令替换为 标头的全部内容。如何指定地点或标题 标识是实现定义的。

注意最后一句话。

【讨论】:

  • 它没有完全回答这个问题。如果可以,请编辑。
猜你喜欢
  • 2020-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多