47

There are two path separators in common use: the Unix forward-slash and the DOS backslash. Rest in peace, Classic Mac colon. If used in an #include directive, are they equal under the rules of the C++11, C++03, and C99 standards?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421

6 Answers6

51

C99 says (§6.4.7/3):

If the characters ', \, ", //, or /* occur in the sequence between the < and > delimiters, the behavior is undefined. Similarly, if the characters ', \, //, or /* occur in the sequence between the " delimiters, the behavior is undefined.

(footnote: Thus, sequences of characters that resemble escape sequences cause undefined behavior.)

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

If either of the characters ’ or \, or either of the character sequences /* or // appears in a q-char- sequence or a h-char-sequence, or the character " appears in a h-char-sequence, the behavior is undefined.

(footnote: Thus, sequences of characters that resemble escape sequences cause undefined behavior.)

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

The appearance of either of the characters ’ or \ or of either of the character sequences /* or // in a q-char-sequence or an h-char-sequence is conditionally supported with implementation-defined semantics, as is the appearance of the character " in an h-char-sequence.

(footnote: Thus, a sequence of characters that resembles an escape sequence might result in an error, be interpreted as the character corresponding to the escape sequence, or have a completely different meaning, depending on the implementation.)

Therefore, although any compiler might choose to support a backslash in a #include path, it is unlikely that any compiler vendor won't support forward slash, and backslashes are likely to trip some implementations up by virtue of forming escape codes. (Edit: apparently MSVC previously required backslash. Perhaps others on DOS-derived platforms were similar. Hmmm… what can I say.)

C++11 seems to loosen the rules, but "conditionally supported" is not meaningfully better than "causes undefined behavior." The change does more to reflect the existence of certain popular compilers than to describe a portable standard.

Of course, nothing in any of these standards says that there is such a thing as paths. There are filesystems out there with no paths at all! However, many libraries assume pathnames, including POSIX and Boost, so it is reasonable to want a portable way to refer to files within subdirectories.

Community
  • 1
  • 1
Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
  • Even if, from a purely-requirements-driven standpoint, "Conditionally supported behavior" and "Undefined behavior" would impose identical obligations on a compiler (i.e. none), the former has an implication that platforms where something would have a sensible meaning *should* support that meaning, even though the Standard does not require any particular implementation to honor any particular meaning. It's too bad that the Standard didn't apply such treatment to many other forms of UB to which many platforms have historically attached useful meanings. – supercat Jul 01 '15 at 16:24
  • @supercat Good point. That's actually one of the guiding principles of my proposed [comprehensive preprocessor spec revision](http://bit.ly/pprev2). Unfortunately, it seems to be trapped in committee bureaucracy :( . – Potatoswatter Jul 02 '15 at 00:55
  • @Potatoswatter: What do you think of the notion of replacing many other forms of UB with "conditionally-supported behavior", and adding a means by which code can test support (and either use alternative slower algorithms or refuse compilation when unavailable)? Many compilers have command-line switches which define behaviors in cases where the Standard does not, but there is at present no way for source code to confirm that the switches are set suitably. Further, in some cases, the code required to achieve correctness in an optimized build may be horribly and needlessly inefficient... – supercat Jul 02 '15 at 13:06
  • ...in an unoptimized one (e.g. an optimizer may realize that `for (int i=0; i=ptr1 && ptr2 – supercat Jul 02 '15 at 13:15
  • @supercat Sounds reasonable. You might float it at the official [std-proposals](https://groups.google.com/a/isocpp.org/forum/#!forum/std-proposals) list. If you want to present it formally, I'd suggest going through the Reflection study group first, because they recommend feature-testing macros. The UB study group seems to be less productive, and their bureaucracy is what stalled my proposal. In Feb 2014 they promised to review it and didn't meet. In Nov 2014 they met, but only briefly, and deferred any review because it was too big. It was a bit disorganized. – Potatoswatter Jul 03 '15 at 01:46
  • "apparently MSVC previously required backslash" ... in strings passed to fopen at runtime, yes. But I'm pretty sure not #include. The earliest releases of Microsoft C came with a header file that could be accessed with `#include `, because there was much code being ported to it which used that exact form. Maybe I recall incorrectly, but then you have to ask yourself how thick they would have to be, to supply that file for compatibility, and then make you change the source to use it. Recall this was the days when there was far far more code being ported *to* MSC... – greggo Nov 10 '15 at 19:32
8

Forward slash is the correct way; the pre-compiler will do whatever it takes on each platform to get to the correct file.

trojanfoe
  • 120,358
  • 21
  • 212
  • 242
  • Sadly, only in includes... MSVC used to be bitchy about forward/backward slashes in filepathes in strings, I'm thankful it isn't anymore. – Xeo Apr 26 '11 at 12:23
  • 3
    @Xeo That isn’t dependent on MSVC, it’s Windows itself: modern Windowses accept forward slash as a path separator; Windows 98 didn’t (AFAIR). – Konrad Rudolph Apr 26 '11 at 12:25
  • @Konrad: Most of the problems stem from the fact that Windows command line tools like to use '/' to signify command line arguments, rather than the UNIX '-' or '--'. – trojanfoe Apr 26 '11 at 12:27
  • 2
    There is absolutely _nothing_ in the standard that says forward slash is mandated, nor that a "pre-compiler" (I assume you're talking about the preprocessor phase of the compiler here) will magically turn it into whatever is necessary. Almost the entire thing is implementation-defined. – paxdiablo Apr 26 '11 at 12:36
  • @KonradRudolph we could target that blame a bit more accurately at microsoft's stdlib implementation; fopen can change "/" to "\" . ... – greggo Nov 12 '15 at 18:51
  • 1
    @trojanfoe yes, inherited from CP/M, before paths existed. I happen to think that using "\" for a path separator in DOS 2 was one of the worst decisions in the history of computing. The 'compatibility issue' this solved re command line switches was perceived & invented rather than actual, since that applied only to extant .com programs which were not even aware of the new API that allowed paths to be specified. And the chaos relative to "\ is universally escape" in other important OS, to which they were clearly trying to generally migrate, was totally foreseeable. – greggo Nov 12 '15 at 18:56
8

It depends on what you mean by "acceptable".

There are two senses in which slashes are acceptable and backslashes are not.

If you're writing C99, C++03, or C1x, backslashes are undefined, while slashes are legal, so in this sense, backslashes are not acceptable.

But this is irrelevant for most people. If you're writing C++1x, where backslashes are conditionally-supported, and the platform you're coding for supports them, they're acceptable. And if you're writing an "extended dialect" of C99/C++03/C1x that defines backslashes, same deal. And, more importantly, this notion of "acceptable" is pretty meaningless in most cases anyway. None of the C/C++ standards define what slashes mean (or what backslashes mean when they're conditionally-supported). Header names are mapped to source files in an implementation-defined manner, period. If you've got a hierarchy of files, and you're asking whether to use backslashes or slashes to refer to them portably in #include directives, the answer is: neither is portable. If you want to write truly portable code, you can't use hierarchies of header files—in fact, arguably, your best bet is to write everything in a single source file, and not #include anything except standard headers.

However, in the real world, people often want "portable-enough", not "strictly portable". The POSIX standard mandates what slashes mean, and even beyond POSIX, most modern platforms—including Win32 (and Win64), the cross-compilers for embedded and mobile platforms like Symbian, etc.—treat slashes the POSIX way, at least as far as C/C++ #include directives. Any platform that doesn't, probably won't have any way for you to get your source tree onto it, process your makefile/etc., and so on, so #include directives will be the least of your worries. If that's what you care about, then slashes are acceptable, but backslashes are not.

abarnert
  • 354,177
  • 51
  • 601
  • 671
  • While implementations were not required to specify what, if anything, they did with backslashes, any *quality* implementation targeting a platform that required backslashes in file names would specify how it treated them. The C Standard says nothing about how compilers should handle any project that spans multiple directories, but instead relies upon implementations for various platforms to behave in ways appropriate to those platforms. – supercat Feb 04 '19 at 22:20
6

Blackslash is undefined behavior and even with a slash you have to be careful. The C99 standard states:

If the characters ', \, ", //, or /* occur in the sequence between the < and > delimiters, the behavior is undefined. Similarly, if the characters ', \, //, or /* occur in the sequence between the " delimiters, the behavior is undefined.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
2

Always use forward slashes - they work on more platforms. Backslash technically causes undefined behaviour in C++03 (2.8/2 in the standard).

Alan Stokes
  • 18,815
  • 3
  • 45
  • 64
  • 3
    They do _not_ work on every platform. Some platforms don't have `/` as the directory separator. Backslashes are now implementation defined behaviour in C++0x but then, so is most of the other stuff surrounding includes. – paxdiablo Apr 26 '11 at 12:28
1

The standard says for #include that it:

searches a sequence of implementation-defined places for a header identified uniquely by the specified sequence between the delimiters, and causes the replacement of that directive by the entire contents of the header. How the places are specified or the header identified is implementation-defined.

Note the last sentence.