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

Posted

技术标签:

【中文标题】cout << "\\n"[a==N]; 是啥意思?做?【英文标题】:what does cout << "\n"[a==N]; do?cout << "\n"[a==N]; 是什么意思?做? 【发布时间】:2015-09-02 12:39:08 【问题描述】:

在以下示例中:

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

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

【问题讨论】:

【参考方案1】:

我不知道 [] 选项在 cout 中的作用

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

\n\0

注意\0 被附加到所有字符串文字中。

== 运算符导致 truefalse,因此索引将为:

0 如果为假,如果 a 不等于 N 导致 \n 1 如果为真,如果 a 等于 N 导致 \0

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

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

字符串字面量具有类型“n const char 数组”,其中 n 是 字符串的大小如下定义,并且具有静态存储持续时间 (3.7)。

和:

在任何必要的连接之后,在翻译阶段 7 (2.2) 中, '\0' 附加到每个字符串文字,以便扫描字符串的程序可以找到它的结尾。

4.5 部分 [conv.prom] 说:

bool 类型的纯右值可以转换为 int 类型的纯右值,用 假变零,真变一。

将空字符写入文本流

声称将空字符 (\0) 写入文本流是未定义的行为。

据我所知,这是一个合理的结论,cout 是根据 C 流定义的,正如我们从 27.4.2 [narrow.stream.objects] 中看到的那样:

对象 cout 控制输出到与对象 stdout 关联的流缓冲区,在 (27.9.2).

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

[...]从文本流中读取的数据必须与数据比较 仅在以下情况下才写入该流:数据仅包含打印 字符和控制字符水平制表符和换行符;

打印字符覆盖在7.4字符处理

[...]术语控制字符 指的是特定于语言环境的字符集的成员,这些字符不打印 characters.199) 所有字母和数字都是打印字符。

脚注199 说:

在使用七位 US ASCII 字符集的实现中,打印字符是那些 其值从 0x20(空格)到 0x7E(波浪号);控制字符是那些 值从 0 (NUL) 到 0x1F (US),字符 0x7F (DEL)。

最后我们可以看到没有指定发送空字符的结果,我们可以从4 Conformance 部分看到这是未定义的行为:

[...]否则未定义的行为 在本国际标准中用“未定义行为”或 省略任何明确的行为定义。[...]

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

需要在文本流 I/O 中保留的字符集是编写 C 所需的字符集 程式;其目的是标准应该允许 C 翻译器以最大程度地编写 便携时尚。为此目的不需要诸如退格之类的控制字符,因此它们的 文本流中的处理不是强制性的。

【讨论】:

嗯更好,虽然你仍然引用了一些不是标准的东西:(如果它是一个 FDIS,那么你可以假装你从紧随其后的国际标准中提取了文本(根据定义,在内容),但否则我认为您不应该使用它。如果有帮助,我可以确认 C++14 在具有相同编号的部分中按原样包含此措辞。[编辑:实际上,n3936 C++14 FDIS! 所以我认为你可以更新标签以引用 C++14] 注意,我添加了这个答案,因为当时虽然有几个答案,但奇怪的是,没有人解释什么是字符串文字以及为什么对它进行索引是有效的。一旦明确了这一点,其余的就会随之而来。 @KarolyHorvath 除了条件的一个分支是什么都不做,需要在三元表达式中使用相当难看的占位符文字"" 这个答案应该解决cout &lt;&lt; '\0'的作用是什么 @LightnessRacesinOrbit 这是否意味着您现在将放弃抱怨人们引用 N3936 而不是购买 C++14 的副本?【参考方案2】:
cout<<"\n"[a==N];

我不知道 [] 选项在 cout 中的作用

在C++ operator Precedence table中,operator []operator &lt;&lt;绑定得更紧密,所以你的代码相当于:

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

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

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


因为C++ 中的string literals总是以空字符('\0'L'\0'char16_t() 等)终止,所以字符串文字"\n" 是@987654336 @拿着字符'\n''\0'

在内存布局中是这样的:

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

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

它在功能上类似于(不一样):

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

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

"\n"[a==N] 的类型是 const char


如果打算不打印任何内容(这可能与打印 '\0' 不同,具体取决于平台和目的),请首选以下代码行:

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

即使您的意图是在流上写入'\0''\n',也更喜欢可读的代码,例如:

cout << (a == N ? '\0' : '\n');

【讨论】:

与其他示例有什么“不同”?仅仅是副本、错字和范围泄漏吗? 如果打算打印换行符或空字符,您仍然应该更喜欢与原始代码行不同的东西! @LightnessRacesinOrbit 是的,你是对的,它是复制 + 范围泄漏 :) 感谢您指出错字,我现在会修复它。当我说类似时,我的意图是,即使在其他地方没有使用匿名,编译器也可能决定生成不同的代码【参考方案3】:

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

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

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

问题是您不能在文本模式下将'\0' 字符发送到 I/O 流。该代码的作者可能已经注意到似乎没有发生任何事情,因此他认为cout&lt;&lt;'\0' 是一种安全的不做任何事情的方法。

在 C 和 C++ 中,由于未定义行为的概念,这是一个非常糟糕的假设。可以发生。在这种情况下,一个相当可能的结果是流将完全停止工作 - 根本不会再出现到 cout 的输出。

总的来说,效果是,

“如果a 不等于N,则打印一个换行符。否则,我不知道。崩溃什么的。”

……道德是,不要写得这么神秘。

【讨论】:

C++ 或 C 标准中没有关于在文本模式下将 '\0' 发送到 I/O 流是未定义的行为。 “文本模式”是一个 Windows 概念。在基于 Unix 的系统上,文本模式和二进制模式没有区别。 @LightnessRacesinOrbit:实际上,它可能是 Unspecified,并且 IIRC 在某些情况下,无论给定的行为是未定义的还是未定义的(或曾经是)常识。未指定。例如。 int 的大小未定义,但这使其未指定。 @LightnessRacesinOrbit - Re 如果你没有定义某些东西,那么它是未定义的。这可能是它在 C++ 中的工作方式,但在 C 中却不是。C 标准是非常小心地将行为定义为“未定义”。看似矛盾,但我喜欢。 C 标准准确地告诉了我不应该踏足的地方。它甚至给了我所有未定义行为的简短摘要(如果您可以将超过 13 页称为“简短”)。 C++ 标准偶尔会说“UB”,但它没有总结,更糟糕的是,有很多 C++ 行为很可能是未定义的,但没有明确说明。 @LightnessRacesinOrbit - 这是 MSalters 选择的例子,不是我的。手头的问题是写'\0'std::cout 是否是未定义的行为。从我对标准的阅读来看,事实并非如此。仅仅因为 Windows 做了一些不同的事情并不意味着它是 UB。这只是意味着 Windows 再次做了一些不同的事情。 @DavidHammen:我不同意这一点【参考方案4】:

不是cout的选项,而是"\n"的数组索引

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

但是将 nul 传递给 iostream 会产生未定义的结果,最好传递一个字符串:

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

但是,这两种情况下的代码都不是特别可取的,除了混淆之外没有任何特殊用途;不要将其视为良好做法的示例。在大多数情况下,以下是更可取的:

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

或者只是:

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

【讨论】:

第一个示例中不需要括号:cout &lt;&lt; &amp;"\n"[a==N] @eush77 :我知道,但是我们在不知道 & 和 [] 的相对优先级的情况下提供了清晰的服务。【参考方案5】:

以下每一行都会产生完全相同的输出:

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 风格的字符串文字都附加了空字符 '\0'。这意味着"\n" 的内部表示是数组'\n', '\0'

鉴于上述情况,假设 a==N 的计算结果为 false。在这种情况下,行为在所有系统中都是明确定义的:您将得到一个换行符。另一方面,如果a==N 的计算结果为true,则该行为高度依赖于系统。根据 cmets 对问题的回答,Windows 不会喜欢这样。在std::cout 通过管道传送到终端窗口的类Unix 系统上,这种行为是相当良性的。什么都没有发生。

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

【讨论】:

@MarkHurd - 所有四个语句都完全做同样的事情。请阅读索引如何在 C 和 C++ 中的原始数组上工作。关于以文本模式将'\0' 写入输出,这在 unix 和 linux 机器上是完全可以的。它一直在发生。二进制模式,文本模式?那是什么? Unix 和 linux 不区分这两者。 C 和 C++ 标准的某些部分向 Windows 致敬,其他部分向 unix 和 linux 致敬,而其他部分向其他体系结构致敬。不要那么以 Windows 为中心。 我在你的后两个陈述中没有看到你的*。对不起。 @MarkHurd - 我将添加一些间距以使其明显。 从后两个语句中删除* 会导致带有clang 的愚蠢警告。我必须用Wno-string-int 编译才能使编译干净。在前两个语句中添加一个 & 符号具有相同的效果,即使没有该编译器选项也可以编译干净,并且它具有增加混淆级别的额外好处。 (显然由于这个编译器警告,gnu 的 binutils 从 "some_string"+some_int 修改为 &amp;"some_string"[some_int]。) 请注意:上面的评论还有增加混淆级别的额外好处是非常开玩笑的。良好的编程实践意味着避免增加混淆级别。

以上是关于cout << "\n"[a==N]; 是啥意思?做?的主要内容,如果未能解决你的问题,请参考以下文章

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

STL之queue

STL之priority_queue2

cout是啥?

c语言 cout<<"最小数="<<a[1]<<endl; cout<<"最大数="<<a[9]&l

cout具体用法