В следующем примере:
cout<<"\n"[a==N];
Я не знаю, что делает параметр []
в cout
, но он не печатает новую строку, когда значение a
равно N
.
В следующем примере:
cout<<"\n"[a==N];
Я не знаю, что делает параметр []
в cout
, но он не печатает новую строку, когда значение a
равно N
.
Я не знаю, что делает опция [] в 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, не требуются для этой цели, поэтому их обработка в текстовых потоках не предусмотрена.
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');
Вероятно, это был причудливый способ писать
if ( a != N ) {
cout<<"\n";
}
Оператор []
выбирает элемент из массива. Строка "\n"
на самом деле представляет собой массив из двух символов: новую строку '\n'
и ограничитель строк '\0'
. Таким образом, cout<<"\n"[a==N]
будет печатать либо символ '\n'
, либо символ '\0'
.
Проблема заключается в том, что вам не разрешено отправлять символ '\0'
в поток ввода-вывода в текстовом режиме. Автор этого кода мог заметить, что ничего не произошло, поэтому он предположил, что cout<<'\0'
- безопасный способ ничего не делать.
В C и С++ это очень плохое предположение из-за понятия поведения undefined. Если программа делает то, что не покрывается спецификацией стандарта или конкретной платформы, все может случиться. Достаточно вероятный результат в этом случае состоит в том, что поток перестанет работать полностью - больше не будет выводиться на cout
.
Таким образом, эффект:
"Распечатайте новую строку, если
a
не равноN
. В противном случае я не знаю. Ошибка или что-то в этом роде.
... и мораль - это не писать так загадочно.
Это не опция cout
, а индекс массива "\n"
Индекс массива [a==N]
оценивается как [0] или [1] и индексирует массив символов, представленный "\n"
, который содержит строку новой строки и символ nul.
Однако передача nul в iostream будет иметь undefined результаты, и было бы лучше передать строку:
cout << &("\n"[a==N]) ;
Однако код в любом случае не является особенно целесообразным и не служит никакой конкретной цели, кроме как запутывание; не рассматривайте его как пример хорошей практики. В большинстве случаев предпочтительнее:
cout << (a != N ? "\n" : "") ;
или просто:
if( a != N ) cout << `\n` ;
Каждая из следующих строк будет генерировать точно такой же результат:
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
передается в терминальное окно, поведение довольно мягкое. Ничего не происходит.
Просто потому, что вы можете написать такой код, это не значит, что вам нужно. Никогда не пишите такой код.