what does cout << "\n"[a==N]; do?
Asked Answered
I

5

64

In the following example:

cout<<"\n"[a==N];

I have no clue about what the [] option does in cout, but it does not print a newline when the value of a is equal to N.

Indicant answered 17/6, 2015 at 10:26 Comment(0)
T
73

I have no clue about what the [] option does in cout

This is actually not a cout option, what is happening is that "\n" is a string literal. A string literal has the type array of n const char, the [] is simply an index into an array of characters which in this case contains:

\n\0

note \0 is appended to all string literals.

The == operator results in either true or false, so the index will be:

  • 0 if false, if a does not equal N resulting in \n
  • 1 if true, if a equals N resulting in \0

This is rather cryptic and could have been replaced with a simple if.

For reference the C++14 standard(Lightness confirmed the draft matches the actual standard) with the closest draft being N3936 in section 2.14.5 String literals [lex.string] says (emphasis mine):

string literal has type “array of n const char”, where n is the size of the string as defined below, and has static storage duration (3.7).

and:

After any necessary concatenation, in translation phase 7 (2.2), ’\0’ is appended to every string literal so that programs that scan a string can find its end.

section 4.5 [conv.prom] says:

A prvalue of type bool can be converted to a prvalue of type int, with false becoming zero and true becoming one.

Writing a null character to a text stream

The claim was made that writing a null character(\0) to a text stream is undefined behavior.

As far as I can tell this is a reasonable conclusion, cout is defined in terms of C stream, as we can see from 27.4.2 [narrow.stream.objects] which says:

The object cout controls output to a stream buffer associated with the object stdout, declared in <cstdio> (27.9.2).

and the C11 draft standard in section 7.21.2 Streams says:

[...]Data read in from a text stream will necessarily compare equal to the data that were earlier written out to that stream only if: the data consist only of printing characters and the control characters horizontal tab and new-line;

and printing characters are covered in 7.4 Character handling <ctype.h>:

[...]the term control character refers to a member of a locale-specific set of characters that are not printing characters.199) All letters and digits are printing characters.

with footnote 199 saying:

In an implementation that uses the seven-bit US ASCII character set, the printing characters are those whose values lie from 0x20 (space) through 0x7E (tilde); the control characters are those whose values lie from 0 (NUL) through 0x1F (US), and the character 0x7F (DEL).

and finally we can see that the result of sending a null character is not specified and we can see this is undefined behavior from section 4 Conformance which says:

[...]Undefined behavior is otherwise indicated in this International Standard by the words ‘‘undefined behavior’’ or by the omission of any explicit definition of behavior.[...]

We can also look to the C99 rationale which says:

The set of characters required to be preserved in text stream I/O are those needed for writing C programs; the intent is that the Standard should permit a C translator to be written in a maximally portable fashion. Control characters such as backspace are not required for this purpose, so their handling in text streams is not mandated.

Timbuktu answered 17/6, 2015 at 12:15 Comment(10)
Hmm better, though you're still citing something which is not a standard :( If it's an FDIS then you could pretend you pulled the text from the immediately following International Standard (which is by definition identical in content), but otherwise I don't think you should use it. If it helps, I can confirm that C++14 contains this wording as-is in sections with the same numbering. [edit: in fact, n3936 is the C++14 FDIS! so I think you can just update the label to cite C++14]Urogenital
Note, I added this answer because at the time although there were several answers, oddly, none explained what a string literal was and why it was valid to index it. Once this is clear the rest follows.Timbuktu
"could have been replaced with a simple if" - or, preferably, with the ternary operator ?.Telegraph
@KarolyHorvath Except that one branch of the condition is to do nothing, requiring a rather ugly placeholder literal "" in a ternary expression.Ossifrage
This answer should address what the effect of cout << '\0' isUrsa
@LightnessRacesinOrbit does this mean that you will now give up complaining that people are quoting N3936 instead of purchasing a copy of C++14?Ursa
@MattMcNabb: Seems like that's the polar opposite of the indication from my prior comment. :) The key is that although you should cite a standard, you don't have to buy one if you have the FDIS.Urogenital
@MattMcNabb agreed, this was unfamiliar territory for me and I just did not get around to hunting down all the pieces but it as far as I can tell it sure seems like undefined behavior.Timbuktu
@ShafikYaghmour seeing your answer, I think cout << '\0'; is worthy of its own question. When something is not specified sometimes we go for a common sense or inferred interpretation, instead of classing it as UB. In this case it doesn't say that writing null and reading it again gets null back; but nothing is said at all about exactly what writing to a stream means anyway. (e.g. what does cout << 'x' do, other than "write 'x' to stream")Ursa
@MattMcNabb I thought about writing a question, my initial thought was to wait and see what response this edit received and if there was strong disagreement then I would follow-up with a question.Timbuktu
O
39
cout<<"\n"[a==N];

I have no clue about what the [] option does in cout

In C++ operator Precedence table, operator [] binds tighter than operator <<, so your code is equivalent to:

cout << ("\n"[a==N]);  // or cout.operator <<("\n"[a==N]);

Or in other words, operator [] does nothing directly with cout. It is used only for indexing of string literal "\n"

For example for(int i = 0; i < 3; ++i) std::cout << "abcdef"[i] << std::endl; will print characters a, b and c on consecutive lines on the screen.


Because string literals in C++ are always terminated with null character('\0', L'\0', char16_t(), etc), a string literal "\n" is a const char[2] holding the characters '\n' and '\0'

In memory layout this looks like:

+--------+--------+
|  '\n'  |  '\0'  |
+--------+--------+
0        1          <-- Offset
false    true       <-- Result of condition (a == n)
a != n   a == n     <-- Case

So if a == N is true (promoted to 1), expression "\n"[a == N] results in '\0' and '\n' if result is false.

It is functionally similar (not same) to:

char anonymous[] = "\n";
int index;
if (a == N) index = 1;
else index = 0;
cout << anonymous[index];

valueof "\n"[a==N] is '\n' or '\0'

typeof "\n"[a==N] is const char


If the intention is to print nothing (Which may be different from printing '\0' depending on platform and purpose), prefer the following line of code:

if(a != N) cout << '\n';

Even if your intention is to write either '\0' or '\n' on the stream, prefer a readable code for example:

cout << (a == N ? '\0' : '\n');
Ossuary answered 17/6, 2015 at 10:28 Comment(3)
In what way is it "not same" to that other example? Is it just the copy, the typo and the scope leaks?Urogenital
If the intention is to print either a newline or a null character, you should still prefer something different from the original line of code!Leclair
@LightnessRacesinOrbit Yes you are right, it is copy + scope leak :) Thanks for pointing the typo, I will fix it now. My intention when I said similar is, Even if anonymous is not used elsewhere, a compiler may decide to generate different code.Ossuary
C
9

It's probably intended as a bizarre way of writing

if ( a != N ) {
    cout<<"\n";
}

The [] operator selects an element from an array. The string "\n" is actually an array of two characters: a new line '\n' and a string terminator '\0'. So cout<<"\n"[a==N] will print either a '\n' character or a '\0' character.

The trouble is that you're not allowed to send a '\0' character to an I/O stream in text mode. The author of that code might have noticed that nothing seemed to happen, so he assumed that cout<<'\0' is a safe way to do nothing.

In C and C++, that is a very poor assumption because of the notion of undefined behavior. If the program does something that is not covered by the specification of the standard or the particular platform, anything can happen. A fairly likely outcome in this case is that the stream will stop working entirely — no more output to cout will appear at all.

In summary, the effect is,

"Print a newline if a is not equal to N. Otherwise, I don't know. Crash or something."

… and the moral is, don't write things so cryptically.

Claudioclaudius answered 17/6, 2015 at 10:33 Comment(17)
There's nothing in the C++ or C standard about sending '\0' to an I/O stream in text mode being undefined behavior. "Text mode" is a Windows concept. There is no distinction between text mode and binary mode on Unix-based systems.Actinia
@DavidHammen The lack of specification is what makes it undefined. See C11 (N1570) §7.21.2/2: "Data read in from a text stream will necessarily compare equal to the data that were earlier written out to that stream only if: the data consist only of printing characters and the control characters horizontal tab and new-line; no new-line character is immediately preceded by space characters; and the last character is a new-line character. Whether space characters that are written out immediately before a new-line character appear when read in is implementation-defined."Claudioclaudius
(The behavior of C++ iostreams is defined in terms of C stdio streams.)Claudioclaudius
Whether "text stream" has any meaning whatsoever is implementation-defined. For example, it has no meaning in POSIX (POSIX fopen ignores the 'b' in open modes "wb", "rb", etc.) Moreover, both the C and C++ standards are very careful to explicitly use the words "undefined behavior" when a program invokes undefined behavior. While there's lots of undefined behavior in C §7.21, the section you quoted does not use the words "undefined behavior". It can't use those dreaded words there because of POSIX and POSIX-like systems.Actinia
@DavidHammen: You're far too optimistic about C++. I remember that back in 2001, one of the ISO C guys challenged the ISO C++ committee to come up with a list of all cases of Undefined Behavior in C++98. Nobody accepted the offer, and there was a wide consensus that a lot of cases were Undefined by omission.Floeter
@MSalters: Yes, of course. If you don't define something, then it is undefined. I would have thought this painfully obvious.Urogenital
@LightnessRacesinOrbit: Actually, it may be Unspecified, and IIRC there are a few cases where it is (or was) left to common sense whether a given behavior was Undefined or Unspecified. E.g. the size of int is not defined, but that makes it unspecified.Floeter
@MSalters: 3.9.2 "Plain ints have the natural size suggested by the architecture of the execution environment" and "In this list, each type provides at least as much storage as those preceding it in the list". That's why it's unspecified, but there are certainly semantics defined here (it's just done by indirection, that's all). That's why it's not UB.Urogenital
@Floeter - I'm far too pessimistic about C++. The C standard has long has appendix J.2, which now spans thirteen+ pages. I haven't the foggiest idea how long the corresponding list for C++ would be because such an appendix does not exist in the C++ standard. When I search the C++ standard for "undefined" I get lots of hits. Lots and lots and lots of hits.Actinia
@LightnessRacesinOrbit - Re If you don't define something, then it is undefined. That might be how it works in C++, but not in C. The C standard is very careful to define behaviors as "undefined". It seems contradictory, but I like it. The C standard tells me exactly where I should not tread. It even gives me a nice short (if you can call thirteen+ pages "short") summary of all undefined behaviors. The C++ standard sporadically does say "UB", but it doesn't summarize, and to make it worse, there are lots of C++ behaviors that most likely are undefined but are not specified as such.Actinia
@DavidHammen: I was talking about C++.Urogenital
@LightnessRacesinOrbit - The C++ standard talks about C. As potatoswatter wrote, the C++ streams are implemented in terms of C stdio streams.Actinia
@DavidHammen: Yet C++ tells us what the size of plain int should be (ish), which was your chosen example. C++ is what you and I were talking about. ;) #handwaveUrogenital
@LightnessRacesinOrbit - That was MSalters chosen example, not mine. The problem at hand is whether writing '\0' to std::cout is undefined behavior. From my reading of the standards, it's not. Just because Windows does something different with that doesn't mean that it's UB. It just means that Windows once again is doing something different.Actinia
@DavidHammen: I don't disagree with thatUrogenital
@DavidHammen C11 §4/2: "Undefined behavior is otherwise indicated in this International Standard by the words ‘‘undefined behavior’’ or by the omission of any explicit definition of behavior."Claudioclaudius
Also, even if the permissible behaviors are limited to outputting the NUL byte or swallowing it, it's still morally wrong to treat it as a no-op.Claudioclaudius
E
8

It is not an option of cout but an array index of "\n"

The array index [a==N] evaluates to [0] or [1], and indexes the character array represented by "\n" which contains a newline and a nul character.

However passing nul to the iostream will have undefined results, and it would be better to pass a string:

cout << &("\n"[a==N]) ;

However, the code in either case is not particularly advisable and serves no particular purpose other than to obfuscate; do not regard it as an example of good practice. The following is preferable in most instances:

cout << (a != N ? "\n" : "") ;

or just:

if( a != N ) cout << `\n` ;
Ermina answered 17/6, 2015 at 10:37 Comment(2)
You don't need parentheses in your first example: cout << &"\n"[a==N]Psychogenic
@Psychogenic : I know, but clarity us served without knowledge of the relative precedence of & and [].Ermina
A
7

Each of the following lines will generate exactly the same output:

cout << "\n"[a==N];     // Never do this.
cout << (a==N)["\n"];   // Or this.
cout << *((a==N)+"\n"); // Or this.
cout << *("\n"+(a==N)); // Or this.


As the other answers have specified, this has nothing to do with std::cout. It instead is a consequence of

  • How the primitive (non-overloaded) subscripting operator is implemented in C and C++.
    In both languages, if array is a C-style array of primitives, array[42] is syntactic sugar for *(array+42). Even worse, there's no difference between array+42 and 42+array. This leads to interesting obfuscation: Use 42[array] instead of array[42] if your goal is to utterly obfuscate your code. It goes without saying that writing 42[array] is a terrible idea if your goal is to write understandable, maintainable code.

  • How booleans are transformed to integers.
    Given an expression of the form a[b], either a or b must be a pointer expression and the other; the other must be an integer expression. Given the expression "\n"[a==N], the "\n" represents the pointer part of that expression and the a==N represents the integer part of the expression. Here, a==N is a boolean expression that evaluates to false or true. The integer promotion rules specify that false becomes 0 and true becomes 1 on promotion to an integer.

  • How string literals degrade into pointers.
    When a pointer is needed, arrays in C and C++ readily degrade into a pointer that points to the first element of the array.

  • How string literals are implemented.
    Every C-style string literal is appended with the null character '\0'. This means the internal representation of your "\n" is the array {'\n', '\0'}.


Given the above, suppose a==N evaluates to false. In this case, the behavior is well-defined across all systems: You'll get a newline. If, on the other hand, a==N evaluates to true, the behavior is highly system dependent. Based on comments to answers to the question, Windows will not like that. On Unix-like systems where std::cout is piped to the terminal window, the behavior is rather benign. Nothing happens.


Just because you can write code like that doesn't mean you should. Never write code like that.

Actinia answered 17/6, 2015 at 15:42 Comment(6)
@MarkHurd - All four statements do exactly the same thing. Please read up on how indexing works on primitive arrays in C and in C++. With regard to writing '\0' to the output in text mode, that is perfectly okay on unix and linux machines. It happens all the time. Binary mode, text mode? What's that? Unix and linux do not distinguish between the two. Some parts of the C and C++ standards kowtow to Windows, other parts kowtow to unix and linux, yet other parts to yet other architectures. Don't be so Windows-centric.Actinia
I didn't see your * in your second two statements. Sorry.Krigsman
@MarkHurd - I'll add some spacing to make it obvious.Actinia
And now I can say, if you removed the * from the second two statements, it would work better for all environments :-)Krigsman
Removing the * from the second two statements results in a goofy warning with clang. I'd have to compile with Wno-string-int to make that compile clean. Adding an ampersand to the first two statements has the same effect, compiles clean even without that compiler option, and it has the added benefit of increasing the obfuscation level. (Apparently gnu's binutils were modified from "some_string"+some_int to &"some_string"[some_int] because of this compiler warning.)Actinia
Note well: the above comment and it has the added benefit of increasing the obfuscation level was very much tongue-in-cheek. Good programming practices means avoiding increasing the obfuscation level.Actinia

© 2022 - 2024 — McMap. All rights reserved.