Nel seguente esempio:cosa fa cout << " n" [a == N]; fare?
cout<<"\n"[a==N];
non ho idea di quello che l'opzione []
fa in cout
, ma non stampa un ritorno a capo quando il valore di a
è pari a N
.
Nel seguente esempio:cosa fa cout << " n" [a == N]; fare?
cout<<"\n"[a==N];
non ho idea di quello che l'opzione []
fa in cout
, ma non stampa un ritorno a capo quando il valore di a
è pari a N
.
non ho idea di quello che l'opzione [] fa in cout
Questo non è in realtà un'opzione cout
, ciò che sta accadendo è che "\n"
è un string literal. Una stringa letterale ha il tipo array di n const char, il []
è semplicemente un indice in un array di caratteri che in questo caso contiene:
\n\0
nota \0
viene aggiunto a tutti i letterali stringa.
I ==
risultati operatore in entrambi vero o falsa, quindi l'indice sarà:
0
se falsa, se a
non è uguale N
conseguente \n
1
se vero, se a
è uguale a N
risultante in \0
Questo è piuttosto criptico e avrebbe potuto essere sostituito con un semplice if
.
Per riferimento lo standard C++ 14 (Leggerezza confermato il progetto corrisponde allo standard attuale) con il progetto più vicina è N3936 nella sezione 2.14.5
stringhe letterali [lex.string] dice (sottolineatura mia):
stringa letterale ha tipo “vettore di n const char”, dove n è la dimensione della stringa come definito di seguito, ed ha durata di conservazione statica (3.7).
e:
Dopo ogni concatenazione necessaria, in fase di traduzione 7 (2.2), '\ 0' viene aggiunto a ogni stringa letterale modo che i programmi che eseguono la scansione di una stringa può trovare la sua fine.
sezione 4.5
[conv.prom] dice:
Un prvalue di tipo bool può essere convertito in un prvalue di tipo int, con falso diventare zero e diventare uno vero.
Scrivi carattere null per un flusso di testo
L'affermazione è stata fatta che scrivere un carattere null (\0
) per un flusso di testo è un comportamento indefinito.
Per quanto posso dire che questo è un ragionevole conclusione, cout
è definito in termini di flusso di C, come si può vedere dalla 27.4.2
[narrow.stream.objects] che dice:
Il l'oggetto cout controlla l'output in un buffer di flusso associato allo stdout dell'oggetto, dichiarato in < cstdio> (27.9.2).
e il progetto di norma C11 nella sezione 7.21.2
Streams dice:
[...] I dati letti in da un flusso di testo sarà necessariamente confrontare uguali ai dati che sono stati in precedenza scritti fuori a quel flusso solo se: i dati consistono solo nella stampa dei caratteri e nella scheda orizzontale dei caratteri di controllo e nella nuova riga;
e caratteri di stampa sono coperti nella gestione 7.4
caratteri < ctype.h>:
[...] il carattere di controllo termine si riferisce a un membro di un locale-specifica set di caratteri che non vengono stampati caratteri.199) Tutte le lettere e le cifre stampano caratteri.
alla nota 199
dire:
In un'implementazione che utilizza gli USA set di caratteri ASCII a sette bit, i caratteri di stampa sono quelli i cui valori si trovano da 0x20 (spazio) attraverso 0x7E (tilde) ; i caratteri di controllo sono quelli i cui valori si trovano da 0 (NUL) a 0x1F (Stati Uniti) e il carattere 0x7F (CANC).
e, infine, possiamo vedere che il risultato di invio di un carattere null non è specificato e possiamo vedere questo è un comportamento indefinito dalla sezione 4
conformità che dice:
[...] Comportamento indefinito altrimenti indicato nella presente norma internazionale dalle parole '' comportamento non definito '' o dalla omissione di qualsiasi definizione esplicita di comportamento. [...]
Possiamo anche guardare al C99 rationale che dice:
Il set di caratteri richiesto per essere conservati nell'I/O del flusso di testo sono quelli necessari per scrivere i programmi C ; l'intento è che lo Standard dovrebbe consentire a un traduttore C di essere scritto in modo massimamente portatile. I caratteri di controllo come backspace non sono richiesti per questo scopo, pertanto la loro gestione di nei flussi di testo non è obbligatoria.
cout<<"\n"[a==N];
non ho idea di quello che l'opzione [] fa in cout
In C++ operator Precedence table, operator []
si lega più stretto di operator <<
, in modo che il codice è equivalente a:
cout << ("\n"[a==N]); // or cout.operator <<("\n"[a==N]);
O in altre parole, operator []
non fa nulla direttamente con cout
. Viene utilizzato solo per l'indicizzazione di stringhe letterali "\n"
Ad esempio for(int i = 0; i < 3; ++i) std::cout << "abcdef"[i] << std::endl;
stamperà i caratteri a, bec da righe consecutive sullo schermo.
Poiché string literals in C++
sono sempre terminato con carattere nullo ('\0'
, L'\0'
, char16_t()
, ecc), una stringa letterale "\n"
è un const char[2]
tiene i caratteri '\n'
e '\0'
In layout della memoria questo assomiglia a:
+--------+--------+
| '\n' | '\0' |
+--------+--------+
0 1 <-- Offset
false true <-- Result of condition (a == n)
a != n a == n <-- Case
Quindi se a == N
è true (promosso a 1), l'espressione "\n"[a == N]
produce '\0'
e '\n'
se il risultato è falso.
È funzionalmente simile (non uguale) a:
char anonymous[] = "\n";
int index;
if (a == N) index = 1;
else index = 0;
cout << anonymous[index];
valueOf "\n"[a==N]
è '\n'
o '\0'
typeof "\n"[a==N]
è const char
Se l'intenzione è quella di stampare nulla (Che potrebbe essere diverso dalla stampa '\0'
dipendente su piattaforma e finalità), preferiscono la seguente riga di codice:
if(a != N) cout << '\n';
Anche se la vostra intenzione è quella di scrivere sia '\0'
o '\n'
sul torrente, preferiscono un codice leggibile per esempio:
cout << (a == N ? '\0' : '\n');
In che modo "non è uguale" a quell'altro esempio? E 'solo la copia, l'errore di battitura e le perdite di visibilità? –
Se l'intenzione è di stampare una nuova riga o un carattere null, si dovrebbe comunque preferire qualcosa di diverso dalla linea di codice originale! – Hurkyl
@LightnessRacesinOrbit Sì, hai ragione, è copy + scope leak :) Grazie per aver segnalato l'errore, lo aggiusterò ora. La mia intenzione quando ho detto simili è, * Anche se l'anonimo non è usato altrove, un compilatore può decidere di generare un codice diverso *. –
E 'probabilmente inteso come un modo bizzarro di scrivere
if (a != N) {
cout<<"\n";
}
Il []
l'operatore seleziona un elemento da una matrice. La stringa "\n"
è in realtà una matrice di due caratteri: una nuova riga '\n'
e una stringa terminatore '\0'
. Quindi cout<<"\n"[a==N]
stamperà un carattere '\n'
o un carattere '\0'
.
Il problema è che non è consentito inviare un carattere '\0'
a un flusso I/O in modalità testo. L'autore di quel codice potrebbe aver notato che non sembrava accadere nulla, quindi ha pensato che cout<<'\0'
fosse un modo sicuro per non fare nulla.
In C e C++, si tratta di un'ipotesi molto scarsa a causa della nozione di comportamento non definito . Se il programma fa qualcosa che non è coperto dalle specifiche dello standard o della piattaforma particolare, può succedere di tutto. Un risultato abbastanza probabile in questo caso è che lo stream smetterà di funzionare completamente - non verrà più visualizzato alcun output su cout
.
In sintesi, l'effetto è,
"Stampa un ritorno a capo, se
a
non è uguale aN
. In caso contrario, non lo so. Crash o qualcosa del genere."
... e la morale è, non scrivere le cose in modo così criptico.
Non c'è nulla nello standard C++ o C sull'invio di '\ 0' a un flusso I/O in modalità testo che è un comportamento non definito. "Modalità testo" è un concetto di Windows. Non c'è distinzione tra modalità testo e modalità binaria su sistemi basati su Unix. –
@DavidHammen La mancanza di specifiche è ciò che lo rende indefinito. Vedi C11 (N1570) §7.21.2/2: "I dati letti da un flusso di testo saranno necessariamente paragonabili ai dati precedentemente scritti su quel flusso solo se: i dati consistono solo di caratteri di stampa e caratteri di controllo orizzontali tab e new-line, nessun carattere di nuova riga è immediatamente preceduto da caratteri di spazio e l'ultimo carattere è un carattere di nuova riga.Se i caratteri di spazio che vengono scritti immediatamente prima che un carattere di nuova riga compaia quando vengono letti è l'implementazione- definito." – Potatoswatter
(Il comportamento degli iostreams C++ è definito in termini di flussi C stdio.) – Potatoswatter
Non è un'opzione di cout
ma un indice di matrice di "\n"
L'indice di matrice [a==N]
viene valutato come [0] o [1], e indici la matrice carattere rappresentato dal "\n"
che contiene una nuova riga e un nul carattere.
Tuttavia passando nul al iostream avrà risultati indefiniti, e sarebbe meglio passare una stringa:
cout << &("\n"[a==N]) ;
Tuttavia, il codice in entrambi i casi non è particolarmente consigliabile e serve nessun particolare scopo diverso offuscare; non considerarlo come un esempio di buone pratiche. Quanto segue è preferibile nella maggior parte dei casi:
cout << (a != N ? "\n" : "") ;
o solo:
if(a != N) cout << `\n` ;
Ciascuno dei seguenti linee genererà esattamente lo stesso output:
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.
Come le altre risposte hanno indicato, questo non ha nulla a che fare con std::cout
. E invece è una conseguenza di
Come il primitivo (non sovraccaricato) operatore di indicizzazione è implementata in C e C++.
In entrambe le lingue, se array
è una matrice di primitivi in stile C, array[42]
è zucchero sintattico per *(array+42)
. Ancora peggio, non c'è differenza tra array+42
e 42+array
. Questo porta a un'interessante offuscazione: usa 42[array]
invece di array[42]
se il tuo obiettivo è quello di offuscare completamente il tuo codice. Va da sé che scrivere 42[array]
è una pessima idea se il tuo obiettivo è scrivere codice comprensibile e gestibile.
Come i booleani vengono trasformati in numeri interi.
Dato un'espressione del modulo a[b]
, sia a
o deve essere un'espressione di puntatore e l'altro; l'altro deve essere un'espressione intera. Data l'espressione "\n"[a==N]
, lo "\n"
rappresenta la parte puntatore di tale espressione e a==N
rappresenta la parte intera dell'espressione. Qui, a==N
è un'espressione booleana che restituisce false
o true
. Le regole di promozione intera specificano che false
diventa 0 e true
diventa 1 in promozione su un numero intero.
Come i letterali di stringa si degradano in puntatori.
Quando è necessario un puntatore, gli array in C e C++ si degradano facilmente in un puntatore che punta al primo elemento dell'array.
Come vengono convertiti i letterali di stringa.
Ogni stringa letterale stile C viene aggiunta al carattere nullo '\0'
. Ciò significa che la rappresentazione interna del tuo "\n"
è la matrice {'\n', '\0'}
.
luce di quanto sopra, supponiamo a==N
viene valutato come false
. In questo caso, il comportamento è ben definito in tutti i sistemi: otterrai una nuova riga.Se, d'altra parte, a==N
valuta true
, il comportamento dipende molto dal sistema. Sulla base dei commenti alle risposte alla domanda, a Windows non piacerà. Sui sistemi Unix in cui std::cout
viene collegato alla finestra del terminale, il comportamento è piuttosto benigno. Non accade nulla.
Solo perché è possibile scrivere codice come quello non significa che si dovrebbe. Non scrivere mai un codice del genere.
@MarkHurd - Tutte e quattro le affermazioni fanno ** esattamente ** la stessa cosa. Si prega di leggere come funziona l'indicizzazione su array primitivi in C e in C++. Per quanto riguarda la scrittura di ''\ 0'' sull'output in modalità testo, è perfettamente su macchine unix e linux. Succede tutto il tempo. Modalità binaria, modalità testo? Cos'è quello? Unix e Linux non distinguono tra i due. Alcune parti degli standard C e C++ portano a Windows, altre parti si uniscono a unix e linux, altre ancora a altre architetture. Non essere così incentrato su Windows. –
Non ho visto il tuo '*' nelle tue seconde dichiarazioni. Scusate. –
@MarkHurd - Aggiungerò un po 'di spaziatura per renderlo evidente. –
@LightnessRacesinOrbit aggiunta bozza referenziata –
Hmm meglio, anche se stai ancora citando qualcosa che non è uno standard :(Se si tratta di un FDIS allora potresti fingere di aver tirato il testo dallo standard internazionale immediatamente successivo (che è per definizione identico nel contenuto), ma per il resto non penso che dovresti usarlo. Se aiuta, posso confermare che C++ 14 contiene questa dicitura così com'è in sezioni con la stessa numerazione. [[edit: infatti, n3936 ** è ** il C++ 14 FDIS! quindi penso che puoi semplicemente aggiornare l'etichetta per citare C++ 14] _ –
Nota, ho aggiunto questa risposta perché al momento anche se ci sono state diverse risposte, stranamente, nessuno ha spiegato che cosa una stringa era letterale e perché era valido indicizzarlo, una volta che questo è chiaro il resto segue: –