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

Siv*_*sad 60 c++ cout string-literals

在以下示例中:

cout<<"\n"[a==N];
Run Code Online (Sandbox Code Playgroud)

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

Sha*_*our 70

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

这实际上不是一个cout选项,正在发生的事情是,"\n"是一个文字字符串.字符串文字的类型数组为n const char,[]它只是字符数组的索引,在本例中包含:

\n\0
Run Code Online (Sandbox Code Playgroud)

note \0附加到所有字符串文字.

==操作的结果无论是,因此指数将是:

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

这是相当神秘的,可以用简单的替换if.

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

string literal的类型为"const of n const char",其中n是下面定义的字符串的大小,并且具有静态存储持续时间(3.7).

和:

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

部分4.5 [conv.prom]说:

bool类型的prvalue可以转换为int类型的prvalue,false变为零,true变为1.

将空字符写入文本流

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

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

对象cout控制输出到与对象标准输出相关联的流缓冲区,在<cstdio>(27.9.2)中声明.

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

[...]从文本流读入的数据必须与之前写入该流的数据相等,只有在以下情况下:数据仅由打印字符和控制字符水平制表符和换行符组成;

字符处理<ctype.h>中介绍了打印字符打印字符:7.4

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

用脚注199说:

在使用7位US ASCII字符集的实现中,打印字符是其值从0x20(空格)到0x7E(代字号)的字符; 控制字符是其值从0(NUL)到0x1F(US)的字符,以及字符0x7F(DEL).

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

[...]未定义的行为在本国际标准中以"未定义的行为"或"省略任何明确的行为定义"一词另有说明.[...]

我们还可以看看C99的基本原理:

需要在文本流I/O中保留的字符集是编写C程序所需的字符集.意图是标准应该允许以最便携的方式编写C语言翻译器.为此目的不需要诸如退格之类的控制字符,因此不强制它们在文本流中的处理.

  • 注意,我添加了这个答案,因为当时虽然有几个答案,但奇怪的是,没有人解释字符串文字是什么以及为什么索引它是有效的.一旦清楚,其余部分就会出现. (3认同)
  • @LightnessRacesinOrbit这是否意味着你现在会放弃抱怨人们引用N3936而不是购买C++ 14的副本? (3认同)

Moh*_*ain 39

cout<<"\n"[a==N];
Run Code Online (Sandbox Code Playgroud)

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

C++运算符Precedence表中,operator []绑定比紧operator <<,所以你的代码相当于:

cout << ("\n"[a==N]);  // or cout.operator <<("\n"[a==N]);
Run Code Online (Sandbox Code Playgroud)

换句话说,operator []什么也不做cout.它仅用于索引字符串文字"\n"

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


因为字符串文字C++始终与空字符终止('\0',L'\0',char16_t()等),文字的字符串"\n"是一个const char[2]保持所述字符'\n''\0'

在内存布局中,这看起来像:

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

因此,如果a == N为true(提升为1),则表达式"\n"[a == N]结果'\0','\n'如果结果为false.

功能相似(不相同):

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

价值"\n"[a==N]'\n''\0'

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


如果打算不打印任何内容('\0'根据平台和目的可能与打印不同),请选择以下代码行:

if(a != N) cout << '\n';
Run Code Online (Sandbox Code Playgroud)

即使您打算在流中编写'\0'或编写'\n'流,也更喜欢可读代码,例如:

cout << (a == N ? '\0' : '\n');
Run Code Online (Sandbox Code Playgroud)

  • 它与其他例子"以何种方式"相同?它只是副本,错字和范围泄漏? (2认同)
  • 如果打算打印换行符或空字符,您仍然应该喜欢与原始代码行不同的东西! (2认同)

Pot*_*ter 9

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

if ( a != N ) {
    cout<<"\n";
}
Run Code Online (Sandbox Code Playgroud)

[]操作者选择从数组中的一个元素.该字符串"\n"实际上是一个包含两个字符的数组:一个新行'\n'和一个字符串终止符'\0'.因此cout<<"\n"[a==N]将打印'\n'字符或'\0'字符.

问题是您不允许'\0'在文本模式下向I/O流发送字符.该代码的作者可能已经注意到似乎没有发生任何事情,因此他认为这cout<<'\0'是一种无所事事的安全方式.

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

总之,效果是,

"如果a不等于,则打印换行N.否则,我不知道.崩溃或其他什么."

...而道德是,不要如此隐秘地写东西.

  • C++或C标准中没有任何内容关于在文本模式下将'\ 0'发送到I/O流是未定义的行为."文本模式"是Windows概念.在基于Unix的系统上,文本模式和二进制模式之间没有区别. (4认同)

Cli*_*ord 8

它不是一个选项,cout而是一个数组索引"\n"

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

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

cout << &("\n"[a==N]) ;
Run Code Online (Sandbox Code Playgroud)

但是,在任何一种情况下的代码都不是特别可取的,除了混淆之外没有任何特殊目的; 不要把它当作良好做法的一个例子.在大多数情况下,以下情况更可取:

cout << (a != N ? "\n" : "") ;
Run Code Online (Sandbox Code Playgroud)

要不就:

if( a != N ) cout << `\n` ;
Run Code Online (Sandbox Code Playgroud)


Dav*_*men 7

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

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.
Run Code Online (Sandbox Code Playgroud)


正如其他答案所指出的那样,这与此无关std::cout.它取而代之的是

  • 如何在C和C++中实现原始(非重载)下标运算符.
    在这两种语言中,if array是一个C风格的基元数组,array[42]是语法糖*(array+42).更糟糕的是,array+42和之间没有区别42+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,则行为高度依赖于系统.根据对问题答案的评论,Windows不会那样.在类似Unix的系统中,std::cout通过管道传送到终端窗口,行为相当温和.什么都没发生.


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