В следующем примере:Что делает cout << " n" [a == N]; делать?
cout<<"\n"[a==N];
Я не имею ни малейшего понятия о том, что опция []
делает в cout
, но он не печатает символ новой строки, когда значение a
равно N
.
В следующем примере:Что делает cout << " n" [a == N]; делать?
cout<<"\n"[a==N];
Я не имею ни малейшего понятия о том, что опция []
делает в cout
, но он не печатает символ новой строки, когда значение a
равно N
.
Я не имею ни малейшего понятия о том, что опция [] делает в соиЬ
Это на самом деле не a cout
вариант, что происходит, что "\n"
является string literal. Строковый литерал имеет тип массив п сопзЬ обугленного, то []
просто индекс в массив символов, который в данном случае содержит:
\n\0
примечание \0
добавляется ко всем строковых литералов.
В ==
результаты оператора в любом истинно или ложной, так что индекс будет:
0
если ложно, если a
не равна N
приводит к \n
1
, если оно истинно, если a
равно N
, в результате чего \0
Это довольно загадочно и могло быть заменено простым if
.
Для справки стандарт C++ 14 (Lightness подтвердил проект соответствует фактическому стандарту) с ближайшим проект является N3936 в разделе 2.14.5
Строковые литералы [lex.string] говорит (внимание мое):
строка символов имеет тип «массив п сопзЬ полукокса», где п является размер строки, как определено ниже, и имеет статический срок хранения (3.7).
и:
После любой необходимой конкатенации, в фазе перевода 7 (2.2), «\ 0» добавляются к каждой строке буквального так, чтобы программы, которые сканируют строки могут найти ее конец.
раздел 4.5
[conv.prom] говорит:
prvalue типа BOOL могут быть преобразованы в prvalue типа междунар с ложным становится нулевым, и правда становится одним.
Дать нулевой символ в текстовый поток
требование было сделано, что написание нулевой символ (\0
) в текстовый поток не определено поведение.
Насколько я могу сказать, что это разумный вывод, cout
определяется в терминах потока C, как мы можем видеть из 27.4.2
[narrow.stream.objects] который говорит:
объект cout управляет выходом в буфер потока, связанный с объектом stdout, объявленным в < cstdio> (27.9.2).
и проект стандарта C11 в разделе 7.21.2
Streams говорит:
[...] Данные чтения из текстового потока обязательно сравнить равны данным , которые были написаны ранее из к этому потоку только в том случае, если: данные состоят только из печати символов и символов управления горизонтальной вкладкой и новой строки;
и печать символов покрыты в обработке 7.4
< Характер ctype.h>:
[...] характер Термин управления относится к члену локализованную набор символов, которые не печатаются characters.199) Все буквы и цифры печатают символы.
с примечанием 199
говоря:
В реализации, которая использует семь-битные США набора ASCII символов, типографские символы являются теми , значения которых лежит от 0x20 (пространств) через 0x7E (тильда) ; управляющими символами являются те, чьи значения лежат от 0 (NUL) до 0x1F (US) и символ 0x7F (DEL).
и, наконец, мы можем видеть, что результат отправки нулевой символ не определен, и мы можем видеть, что это неопределенное поведение из раздела 4
Соответствия, который говорит:
[...] Неопределенное поведение иначе указан в настоящем стандарте слова «» неопределенного поведения «» или в бездействия любого явного определения поведения. [...]
Мы можем также посмотреть на C99 rationale в котором говорится:
Набор символов, которые необходимо сохранить в текстовом потоке ввода-вывода, необходимы для написания программ C ; цель заключается в том, что Стандарт должен допускать, чтобы переводчик C был написан в максимально возможной степени . Управляющие символы, такие как backspace, не требуются для этой цели, поэтому их обработка в текстовых потоках не предусмотрена.
cout<<"\n"[a==N];
Я не имею ни малейшего понятия о том, что опция [] делает в соиЬ
В 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 в последовательных строках на экране.
Поскольку string literals в 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];
valueof "\n"[a==N]
является '\n'
или '\0'
TypeOf "\n"[a==N]
является const char
Если намерение не печатать ничего (Что может отличаться от печати '\0'
в зависимости от на платформе и цели), предпочитают следующую строку кода:
if(a != N) cout << '\n';
Даже если ваше намерение состоит в том, чтобы написать либо '\0'
или '\n'
на потоке, предпочитают читаемый код, например:
cout << (a == N ? '\0' : '\n');
Каким образом это «не одно и то же» для другого примера? Это только копия, опечатка и объем утечек? –
Если вы намерены распечатать новую строку или нулевой символ, вы все равно должны предпочесть что-то отличное от исходной строки кода! – Hurkyl
@LightnessRacesinOrbit Да, вы правы, это копия + область утечки :) Спасибо за указание опечатки, я исправлю это сейчас. Мое намерение, когда я сказал, похоже: * Даже если анонимный не используется в другом месте, компилятор может решить создать другой код *. –
Это, вероятно, задуман как причудливый способ написания
if (a != N) {
cout<<"\n";
}
The []
оператор выбирает элемент из массива. Строка "\n"
на самом деле представляет собой массив из двух символов: новую строку '\n'
и ограничитель строки '\0'
. Таким образом, cout<<"\n"[a==N]
напечатает либо символ '\n'
, либо символ '\0'
.
Проблема в том, что вам не разрешено отправлять символ в поток ввода-вывода в текстовом режиме. Автор этого кода мог заметить, что ничего не произошло, поэтому он предположил, что cout<<'\0'
- безопасный способ ничего не делать.
В C и C++ это очень плохое предположение из-за понятия неопределенного поведения. Если программа выполняет что-то, что не покрывается спецификацией стандарта или конкретной платформы, все может произойти. Достаточно вероятный результат в этом случае состоит в том, что поток перестанет работать полностью - больше нет вывода на cout
.
В целом, эффект,
«Печать новой строки, если
a
не равнаN
. В противном случае, я не знаю. Крах или что-то.»
... а мораль - это не писать так загадочно.
В стандарте C++ или C нет ничего об отправке '\ 0' в поток ввода-вывода в текстовом режиме, который является неопределенным поведением. «Текстовый режим» - это концепция Windows. В системах на основе Unix нет разницы между текстовым режимом и двоичным режимом. –
@DavidHammen Отсутствие спецификации - это то, что делает ее неопределенной. См. C11 (N1570) §7.21.2/2: «Данные, считываемые из текстового потока, обязательно будут сравниваться с данными, которые ранее были записаны в этот поток, только если: данные состоят только из символов печати и символов управления по горизонтали tab и new-line, без символов новой строки сразу предшествуют символы пробела, а последний символ - символ новой строки. Если символы пробела, которые выписаны непосредственно перед появлением символа новой строки, появляются при чтении, определены «. – Potatoswatter
(Поведение C++ iostreams определено в терминах потоков C stdio.) – Potatoswatter
Это не вариант cout
, но индекс массива "\n"
Индекс массива [a==N]
имеет значение [0] или [1], а также индексы массива символов представлен "\n"
, который содержит символ новой строки и NUL персонаж.
Однако прохождение NUL в iostream будет иметь неопределенные результаты, и было бы лучше, чтобы передать строку:
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 и 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++ легко деградируют в указатель, указывающий на первый элемент массива.
Как строковые литералы реализованы.
Каждый строковый литерал C-стиля добавляется с нулевым символом '\0'
. Это означает, что внутреннее представление вашего "\n"
- это массив {'\n', '\0'}
.
Учитывая вышеизложенное, предположим a==N
вычисляет false
. В этом случае поведение хорошо определено во всех системах: вы получите новую строку.Если, с другой стороны, a==N
оценивает true
, поведение сильно зависит от системы. На основе комментариев к ответам на вопрос Windows не понравится. В Unix-подобных системах, где std::cout
подается в оконечное окно, поведение довольно мягкое. Ничего не произошло.
Только потому, что вы можете написать такой код, это не значит, что вы должны. Никогда не пишите такой код.
@MarkHurd - Все четыре утверждения делают ** точно ** то же самое. Прочитайте, как индексирование работает на примитивных массивах в C и на C++. Что касается записи '' \ 0'' на выход в текстовом режиме, это отлично работает на машинах unix и linux. Так происходит все время. Двоичный режим, текстовый режим? Что это? Unix и linux не различают их. Некоторые части стандартов C и C++ kowtow для Windows, другие части kowtow для unix и linux, но другие части для других архитектур. Не будьте так ориентированы на Windows. –
Я не видел ваши '*' в ваших вторых двух утверждениях. Сожалею. –
@MarkHurd - Я добавлю несколько интервалов, чтобы сделать их очевидными. –
@LightnessRacesinOrbit добавлен проект ссылки –
Хм лучше, хотя вы все еще цитируете что-то, что не является стандартом :(Если это FDIS, тогда вы можете притвориться, что вы вытащили текст из сразу следующего международного стандарта (который по определению идентичен по содержанию), но в противном случае я не думаю, что вы должны его использовать. Если это помогает, я могу подтвердить, что C++ 14 содержит эту формулировку как-есть в разделах с одинаковой нумерацией. _ [edit: на самом деле, n3936 ** ** C++ 14 FDIS! поэтому я думаю, что вы можете просто обновить ярлык, чтобы цитировать C++ 14] _ –
Примечание. Я добавил этот ответ, потому что в то время, хотя было несколько ответов, как ни странно, никто не объяснил, что строка Литерал был и почему он был прав, чтобы проиндексировать его. Как только это станет ясно, последует следующее: –