2015-06-17 2 views

ответ

70

Я не имею ни малейшего понятия о том, что опция [] делает в соиЬ

Это на самом деле не 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.2Streams говорит:

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

и печать символов покрыты в обработке 7.4 < Характер ctype.h>:

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

с примечанием 199 говоря:

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

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

[...] Неопределенное поведение иначе указан в настоящем стандарте слова «» неопределенного поведения «» или в бездействия любого явного определения поведения. [...]

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

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

+0

@LightnessRacesinOrbit добавлен проект ссылки –

+0

Хм лучше, хотя вы все еще цитируете что-то, что не является стандартом :(Если это FDIS, тогда вы можете притвориться, что вы вытащили текст из сразу следующего международного стандарта (который по определению идентичен по содержанию), но в противном случае я не думаю, что вы должны его использовать. Если это помогает, я могу подтвердить, что C++ 14 содержит эту формулировку как-есть в разделах с одинаковой нумерацией. _ [edit: на самом деле, n3936 ** ** C++ 14 FDIS! поэтому я думаю, что вы можете просто обновить ярлык, чтобы цитировать C++ 14] _ –

+3

Примечание. Я добавил этот ответ, потому что в то время, хотя было несколько ответов, как ни странно, никто не объяснил, что строка Литерал был и почему он был прав, чтобы проиндексировать его. Как только это станет ясно, последует следующее: –

39
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'); 
+2

Каким образом это «не одно и то же» для другого примера? Это только копия, опечатка и объем утечек? –

+2

Если вы намерены распечатать новую строку или нулевой символ, вы все равно должны предпочесть что-то отличное от исходной строки кода! – Hurkyl

+0

@LightnessRacesinOrbit Да, вы правы, это копия + область утечки :) Спасибо за указание опечатки, я исправлю это сейчас. Мое намерение, когда я сказал, похоже: * Даже если анонимный не используется в другом месте, компилятор может решить создать другой код *. –

9

Это, вероятно, задуман как причудливый способ написания

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

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

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

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

В целом, эффект,

«Печать новой строки, если a не равна N. В противном случае, я не знаю. Крах или что-то.»

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

+4

В стандарте C++ или C нет ничего об отправке '\ 0' в поток ввода-вывода в текстовом режиме, который является неопределенным поведением. «Текстовый режим» - это концепция Windows. В системах на основе Unix нет разницы между текстовым режимом и двоичным режимом. –

+0

@DavidHammen Отсутствие спецификации - это то, что делает ее неопределенной. См. C11 (N1570) §7.21.2/2: «Данные, считываемые из текстового потока, обязательно будут сравниваться с данными, которые ранее были записаны в этот поток, только если: данные состоят только из символов печати и символов управления по горизонтали tab и new-line, без символов новой строки сразу предшествуют символы пробела, а последний символ - символ новой строки. Если символы пробела, которые выписаны непосредственно перед появлением символа новой строки, появляются при чтении, определены «. – Potatoswatter

+0

(Поведение C++ iostreams определено в терминах потоков C stdio.) – Potatoswatter

8

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

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

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

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

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

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

или просто:

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

В первом примере вам не нужны скобки: 'cout << &" \ n "[a == N]' – eush77

+0

@ eush77: Я знаю, но ясность, которую мы обслуживали без знания относительного приоритета & и [] , – Clifford

8

Каждая из следующих строк будет генерировать точно такой же вывод:

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 подается в оконечное окно, поведение довольно мягкое. Ничего не произошло.


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

+0

@MarkHurd - Все четыре утверждения делают ** точно ** то же самое. Прочитайте, как индексирование работает на примитивных массивах в C и на C++. Что касается записи '' \ 0'' на выход в текстовом режиме, это отлично работает на машинах unix и linux. Так происходит все время. Двоичный режим, текстовый режим? Что это? Unix и linux не различают их. Некоторые части стандартов C и C++ kowtow для Windows, другие части kowtow для unix и linux, но другие части для других архитектур. Не будьте так ориентированы на Windows. –

+1

Я не видел ваши '*' в ваших вторых двух утверждениях. Сожалею. –

+0

@MarkHurd - Я добавлю несколько интервалов, чтобы сделать их очевидными. –

Смежные вопросы