cout << "\n"[a==N]; 是什么意思?做?

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

在下面的例子中:

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

我不知道 [] 选项在 cout 中的作用,但是当 a 的值等于 N 时它不会打印换行符.

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

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

C++ operator Precedence table 中,operator [] 绑定比 operator << 更紧密,因此您的代码等同于:

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

或者换句话说,operator [] 不直接对 cout 做任何事情。它仅用于字符串文字的索引 "\n"

例如 for(int i = 0; i < 3; ++i) std::cout << "abcdef"[i] << std::endl; 将在屏幕上的连续行上打印字符 a、b 和 c。


因为 C++ 中的 string literals 总是 以空字符结尾('[=23=]', L'[=24=]', char16_t(), 等), 字符串文字 "\n"const char[2] 包含字符 '\n''[=23=]'

在内存布局中如下所示:

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

因此,如果 a == N 为真(提升为 1),表达式 "\n"[a == N] 结果为 '[=23=]',如果结果为假,则为 '\n'

它在功能上类似于(不相同):

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

"\n"[a==N] 的值是 '\n''[=23=]'

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


如果打算什么都不打印(这可能与打印 '[=23=]' 不同,具体取决于平台和目的),请使用以下代码行:

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

即使你的意图是在流上写'[=23=]''\n',最好是可读的代码,例如:

cout << (a == N ? '[=15=]' : '\n');

这可能是一种奇怪的写作方式

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

[] 运算符从数组中选择一个元素。字符串 "\n" 实际上是一个包含两个字符的数组:换行符 '\n' 和字符串终止符 '[=14=]'。所以 cout<<"\n"[a==N] 将打印 '\n' 个字符或 '[=14=]' 个字符。

问题是您不能在文本模式下将 '[=14=]' 字符发送到 I/O 流。该代码的作者可能已经注意到似乎什么都没发生,所以他认为 cout<<'[=19=]' 是一种什么都不做的安全方法。

在 C 和 C++ 中,由于 未定义行为的概念,这是一个非常糟糕的假设。 如果程序执行标准规范或未涵盖的操作特定的平台,任何事情都有可能发生。在这种情况下,一个很可能的结果是流将完全停止工作——根本不会出现 cout 的输出。

综上所述,效果是,

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

…道德是,不要写得那么隐晦。

不是cout的选项而是"\n"

的数组索引

数组索引[a==N]的计算结果为[0]或[1],并索引由"\n"表示的包含换行符和空字符的字符数组。

然而将 nul 传递给 iostream 将得到未定义的结果,传递一个字符串会更好:

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

但是,这两种情况下的代码都不是特别可取的,并且除了混淆之外没有任何特殊目的;不要将其视为良好实践的范例。在大多数情况下,以下内容更可取:

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

或者只是:

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

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

这实际上不是 cout 选项,实际情况是 "\n"string literal。字符串文字的类型为 array of n const char[] 只是字符数组的索引,在本例中包含:

\n[=10=]

注意 [=14=] 附加到所有字符串文字。

== 运算符结果为 truefalse,因此索引将为:

  • 0 如果为假,如果 a 不等于 N 导致 \n
  • 1 如果为真,如果 a 等于 N 导致 [=14=]

这相当神秘,可以用简单的 if 代替。

参考C++14标准(Lightness确认草案符合实际标准)最接近的草案是N3936部分2.14.5字符串文字 [lex.string] 说(强调我的):

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).

和:

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

部分 4.5 [conv.prom] 说:

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

将空字符写入文本流

声称将空字符 ([=14=]) 写入文本流是未定义的行为。

据我所知这是一个合理的结论,cout是根据C流定义的,从27.4.2 [narrow.stream.objects] 表示:

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

7.21.2 Streams 部分的 C11 标准草案说:

[...]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;

打印字符包含在7.4 字符处理:

[...]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.

带脚注 199 说:

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).

最后我们可以看到未指定发送空字符的结果,我们可以从 4 一致性部分看到这是未定义的行为,它说:

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

我们还可以查看 C99 rationale 上面写着:

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.

以下每一行都将生成完全相同的输出:

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.


正如其他答案所指定的,这与 std::cout 无关。相反,它是

的结果
  • 如何在 C 和 C++ 中实现原始(非重载)下标运算符。
    在这两种语言中,如果 array 是 C 风格的基元数组,那么 array[42] 就是 *(array+42) 的语法糖。更糟糕的是,array+4242+array 之间没有区别。这会导致有趣的混淆:如果您的目标是完全混淆您的代码,请使用 42[array] 而不是 array[42]。不用说,如果您的目标是编写可理解、可维护的代码,那么编写 42[array] 是一个糟糕的主意。

  • 布尔值如何转换为整数。
    给定 a[b] 形式的表达式,ab 必须是指针表达式,另一个必须是;另一个必须是整数表达式。给定表达式 "\n"[a==N]"\n" 表示该表达式的指针部分,a==N 表示表达式的整数部分。这里,a==N 是一个布尔表达式,计算结果为 falsetrue。整数提升规则指定 false 在提升为整数时变为 0,而 true 变为 1。

  • 字符串文字如何退化为指针。
    当需要指针时,C 和 C++ 中的数组很容易退化为指向数组第一个元素的指针。

  • 如何实现字符串文字。
    每个 C 风格的字符串文字都附加了空字符 '[=31=]'。这意味着 "\n" 的内部表示是数组 {'\n', '[=33=]'}.


鉴于上述情况,假设 a==N 的计算结果为 false。在这种情况下,行为在所有系统中都是明确定义的:您将得到一个换行符。另一方面,如果 a==N 的计算结果为 true,则该行为高度依赖于系统。根据对问题答案的评论,Windows 不会这样。在 std::cout 被管道传输到终端 window 的类 Unix 系统上,行为是相当良性的。没有任何反应。


仅仅因为您可以编写这样的代码并不意味着您应该这样做。永远不要写那样的代码。