Что делает cout << "\n" [a == N]; делать?

В следующем примере:

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

Я не знаю, что делает параметр [] в cout, но он не печатает новую строку, когда значение a равно N.

Ответ 1

Я не знаю, что делает опция [] в cout

На самом деле это не параметр cout, что происходит, когда "\n" является строковым литералом. Строковый литерал имеет массив типов n const char, [] - это просто индекс в массив символов, который в этом случае содержит:

\n\0

note \0 добавляется ко всем строковым литералам.

Оператор == выводит либо true, либо false, поэтому индекс будет:

  • 0, если false, если a не равно N, что приводит к \n
  • 1 если true, если a равно N, что приводит к \0

Это довольно загадочно и может быть заменено простым if.

Для справки стандарт С++ 14 (Lightness подтвердил, что черновик соответствует фактическому стандарту), причем ближайший черновик N3936 в разделе 2.14.5 Строковые литералы [lex.string] говорит (внимание мое):

string literal имеет тип "array of n const char" , где n - размер строки, как определено ниже, и имеет статическую продолжительность хранения (3.7).

и

После любой необходимой конкатенации, в фазе перевода 7 (2.2), \0 добавляется к каждому строковому литералу, так что программы, которые сканируют строку, могут найти ее конец.

раздел 4.5 [conv.prom] говорит:

Значение класса bool может быть преобразовано в prvalue типа int, с false становится нулевым и истинным становится единым.

Запись нулевого символа в текстовый поток

Утверждалось, что запись нулевого символа (\0) в текстовый поток - это поведение undefined.

Насколько я могу судить, это разумный вывод, cout определяется в терминах потока C, как мы видим из 27.4.2 [narrow.stream.objects], который гласит:

Объект cout управляет выводом в буфер потока, связанный с объектом stdout, объявленным в <cstdio> (27,9,2).

и проект стандарта C11 в разделе 7.21.2 Потоки говорят:

[...] Данные, считываемые из текстового потока, обязательно будут сравниваться с данными которые ранее были записаны в этот поток, только если: данные состоят только из печати символы и контрольные символы: горизонтальная вкладка и новая строка;

и печатные символы указаны в 7.4 Обработка символов < ctype.h > :

[...] термин управляющий символ относится к члену специфического для локали набора символов, которые не печатаются characters.199) Все буквы и цифры печатают символы.

со сноской 199, говорящей:

В реализации, использующей семибитовый набор символов ASCII US, печатными символами являются те значения которых лежат от 0x20 (пробел) до 0x7E (тильда); контрольные символы - это те, чьи значения лежат от 0 (NUL) до 0x1F (US) и символ 0x7F (DEL).

и, наконец, мы видим, что результат отправки нулевого символа не указан, и мы видим, что это поведение undefined из раздела 4 Соответствие, которое гласит:

[...] Undefined поведение в противном случае указанных в настоящем стандарте, словами "undefined" или отсутствие явного определения поведения. [...]

Мы также можем посмотреть на C99 обоснование, в котором говорится:

Набор символов, которые необходимо сохранить в текстовом потоке ввода/вывода, необходимы для записи C программы; цель заключается в том, что Стандарт должен позволять письменному переводчику C максимально портативный мода. Управляющие символы, такие как backspace, не требуются для этой цели, поэтому их обработка в текстовых потоках не предусмотрена.

Ответ 2

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

Я не знаю, что делает опция [] в cout

В Таблица приоритетов С++, 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++ всегда завершены нулевым символом ('\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

Итак, если 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'

typeof "\n"[a==N] является const char


Если намерение состоит в том, чтобы ничего не печатать (что может отличаться от печати '\0' в зависимости от платформы и цели), предпочитайте следующую строку кода:

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

Даже если ваше намерение состоит в том, чтобы писать либо '\0', либо '\n' в потоке, предпочитайте читаемый код, например:

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

Ответ 3

Вероятно, это был причудливый способ писать

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

Оператор [] выбирает элемент из массива. Строка "\n" на самом деле представляет собой массив из двух символов: новую строку '\n' и ограничитель строк '\0'. Таким образом, cout<<"\n"[a==N] будет печатать либо символ '\n', либо символ '\0'.

Проблема заключается в том, что вам не разрешено отправлять символ '\0' в поток ввода-вывода в текстовом режиме. Автор этого кода мог заметить, что ничего не произошло, поэтому он предположил, что cout<<'\0' - безопасный способ ничего не делать.

В C и С++ это очень плохое предположение из-за понятия поведения undefined. Если программа делает то, что не покрывается спецификацией стандарта или конкретной платформы, все может случиться. Достаточно вероятный результат в этом случае состоит в том, что поток перестанет работать полностью - больше не будет выводиться на cout.

Таким образом, эффект:

"Распечатайте новую строку, если a не равно N. В противном случае я не знаю. Ошибка или что-то в этом роде.

... и мораль - это не писать так загадочно.

Ответ 4

Это не опция cout, а индекс массива "\n"

Индекс массива [a==N] оценивается как [0] или [1] и индексирует массив символов, представленный "\n", который содержит строку новой строки и символ nul.

Однако передача nul в iostream будет иметь undefined результаты, и было бы лучше передать строку:

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

Однако код в любом случае не является особенно целесообразным и не служит никакой конкретной цели, кроме как запутывание; не рассматривайте его как пример хорошей практики. В большинстве случаев предпочтительнее:

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

или просто:

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

Ответ 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 и С++. В обоих языках, если array является массивом примитивов C-стиля, array[42] является синтаксическим сахаром для *(array+42). Хуже того, нет разницы между array+42 и 42+array. Это приводит к интересной обфускации: используйте 42[array] вместо array[42], если ваша цель - полностью запутать ваш код. Само собой разумеется, что писать 42[array] - ужасная идея, если ваша цель - написать понятный, поддерживаемый код.

  • Как логические переменные преобразуются в целые числа. Учитывая выражение формы a[b], либо a, либо b должно быть выражением указателя, а другое; другое должно быть целочисленным выражением. Учитывая выражение "\n"[a==N], "\n" представляет собой часть указателя этого выражения, а a==N представляет собой целую часть выражения. Здесь a==N является булевым выражением, которое вычисляется как false или true. Целые правила продвижения указывают, что false становится 0, а true становится 1 при продвижении по целому числу.

  • Как строковые литералы деградируют в указатели.
    Когда нужен указатель, массивы в C и С++ легко деградируют в указатель, указывающий на первый элемент массива.

  • Как строковые литералы реализованы.
    Каждый строковый литерал C-стиля добавляется с нулевым символом '\0'. Это означает, что внутреннее представление вашего "\n" - это массив {'\n', '\0'}.


Учитывая вышеизложенное, предположим, что a==N имеет значение false. В этом случае поведение хорошо определено во всех системах: вы получите новую строку. Если, с другой стороны, a==N оценивается как true, поведение сильно зависит от системы. На основе комментариев к ответам на вопрос Windows не понравится. В Unix-подобных системах, где std::cout передается в терминальное окно, поведение довольно мягкое. Ничего не происходит.


Просто потому, что вы можете написать такой код, это не значит, что вам нужно. Никогда не пишите такой код.