2015-05-20 44 views
73

ho avuto uno strano bug nel mio programma, e dopo poche ore di debugging, ho trovato la seguente riga di molto stupido:Cosa fa il compilatore qui: int a = b * (c * d * + e)?

int a = b * (c * d * + e) 

Se non lo vedo: Tra d e e ho scritto * +, dove era previsto solo un +.

Perché viene compilato e cosa significa in realtà?

+1

Usi: http://stackoverflow.com/ a/3182557/962089 Inoltre, è possibile stampare (o usare in altro modo) il valore integrale di un tipo di carattere: 'std :: cout << + c;' Se questo accade molto, 'static_cast' ottiene molto clutaggio. – chris

+3

Suggerimento: cosa accadrebbe se avessi intenzione di scrivere un segno meno? – imallett

+0

Cosa succede se il tipo di 'e'? –

risposta

116

Il + viene interpretato come un operatore più unario. Restituisce semplicemente il valore promoted del suo operando.

+13

Per ulteriori dettagli su "promosso", [vedi qui] (http://stackoverflow.com/a/24805967/1505939) –

+5

L'effetto di questo è identico a 'int a = b * (c * d * (+ e)) ' –

26

Come spiegato da Brian, restituisce il valore promosso.
Se fosse un - sarebbe restituire la negazione:

int a = 5; 
int b = 6; 
unsigned int c = 3; 

std::cout << (a * +b); // = 30 
std::cout << (a * -b); // = -30 
std::cout << (1 * -c); // = 4294967293 (2^32 - 3) 
+31

" Valore positivo "è fuorviante. Questo fa sembrare che restituisca il valore assoluto dell'operando, il che non è il caso. –

+8

Neanche '-' restituisce necessariamente" il valore negativo ":' int b = -5; std :: cout << -b; ' – MSalters

+0

@ChrisHayes risposta corretta, grazie –

18

Ciò compila perché il + viene interpretato come più unaria, che eseguirà le promozioni integrali su tipi integrali o enumerazione e il risultato avrà il tipo dell'operando promosso.

Supponendo e è un tipo di enumerazione integrale o senza ambito finirebbe per avere le promozioni integrali applicati comunque dato * applica le conversioni aritmetiche abituali ai suoi operandi quali termina su alle promozioni integrali per tipi integrali.

Dalla bozza di standard C++ 5.3.1[expr.unary.op]:

L'operando dell'operatore unario + avranno l'aritmetica, l'enumerazione senza ambito, o il tipo di puntatore e il risultato è il valore dell'argomento. La promozione integrale viene eseguita su operandi integrali o di enumerazione. Il tipo del risultato è il tipo dell'operando promosso.

Le promozioni integrali sono trattati nella sezione 4.5[conv.prom] e se le variabili e è un tipo diverso bool, char16_t, char32_t, or wchar_t ed hanno conversione rango inferiore int allora sarebbe coperto dal paragrafo 1:

una prvalue di tipo intero diverso da bool, char16_t, char32_t o wchar_t cui conversione rango (4.13) integer è inferiore alla posizione di int può essere convertito in un prvalue di tipo int se int può rep inviato nuovamente tutti i valori del tipo sorgente; in caso contrario, il valore di origine può essere convertito in un valore preregolato di tipo non firmato int.

Per un set completo di casi, è possibile consultare cppreference.

Unario plus può anche essere utile in alcuni casi per risolvere l'ambiguità, un caso interessante sarebbe da Resolving ambiguous overload on function pointer and std::function for a lambda using +.

Nota, per quelle risposte, relativi ai valori unari - e negativi, questo è fuorviante, in quanto questo esempio mostra:

#include <iostream> 

int main() 
{ 
    unsigned x1 = 1 ; 

    std::cout << -x1 << std::endl ; 
} 

che si traduce in:

4294967295 

vederla dal vivo using gcc on wandbox.

E 'interessante notare che unario più è stato aggiunto al C99 per simmetria con unario meno, dal Rationale for International Standard—Programming Languages—C:

unario più è stata adottata dal Comitato C89 da diverse implementazioni, per simmetria con meno unario.

e non posso venire con un buon caso in cui casting non sarebbe sufficiente per ottenere la stessa promozione/conversione desiderata. L'esempio lambda cito sopra, usando più unario per forzare un'espressione lambda da convertire in un puntatore a funzione:

foo(+[](){}); // not ambiguous (calls the function pointer overload) 

potrebbe essere realizzato utilizzando un cast esplicito:

foo(static_cast<void (*)()>([](){})); 

e si potrebbe sostenere questo codice è migliore poiché l'intenzione è esplicita.

Da segnalare che Annotated C++ Reference Manual(ARM) ha il seguente commento:

più unario è un accidente storico e generalmente inutili.

9

Come quello hanno spiegato, (+) e (-) sono stati appena usato come operatore unario:

Unary operators agiscono su un solo operando in un'espressione

int value = 6; 
int negativeInt = -5; 
int positiveInt = +5; 

cout << (value * negativeInt); // 6 * -5 = -30 
cout << (value * positiveInt); // 6 * +5 = 30 

cout << (value * - negativeInt); // 6 * -(-5) = 30 
cout << (value * + negativeInt); // 6 * +(-5) = -30 

cout << (value * - positiveInt); // 6 * -(+5) = -30 
cout << (value * + positiveInt); // 6 * +(+5) = 30 

quindi dal tuo codice:

int b = 2; 
int c = 3; 
int d = 4; 
int e = 5; 

int a = b * (c * d * + e) 

//result: 2 * (3 * 4 * (+5)) = 120 
-1

L'operatore + tra d ed e sarà considerato un operatore unario + che determinerà solo il segno di e. Quindi il compilatore vedrà questa dichiarazione come segue:

int a = b*(c*d*e) ; 
+1

" che determinerà solo il segno di e "Come così? – glglgl

+0

Intendevo dire che il segno + funzionerà come +5, se e = 5. come questo. –

3

questo è solo la matematica di base.Per esempio:

5 * -4 = -20 

5 * +4 = 5 * 4 = 20 

-5 * -4 = 20 

Negativo * Negativo = Positivo

Positivo * Negativo = Negativo

Positivo * Positivo = Positivo

Questa è la spiegazione più semplice c'è .

Il segno meno (-) e più (+) indicano solo se il numero è positivo o negativo.

+1

non è così, che ne dici di questo: 'int a = -5; int val = -a; // result val: 5' –

+1

'--5' diventa' 4': p '- (- 5) = 5'..solo scherzando so che è solo un errore di battitura ... sì hai ragione :) +1 –

4

Perché compila? Compilare perché + viene analizzato come operatore unario più, non l'operatore di addizione. Il compilatore tenta di analizzare il più possibile senza generare errori di sintassi.Quindi questo:

d * + e 

viene analizzato come:

  • d (operando)
  • * (operatore di moltiplicazione)
  • + (unario più operatore)
    • e (operando)

considerando questo:

d*++e; 

viene analizzato come:

  • d (operando)
  • * (operatore di moltiplicazione)
  • ++ (operatore di incremento pre)
    • e (operando)

Inoltre, questo:

d*+++e; 

viene analizzato come:

  • d (operando)
  • * (operatore di moltiplicazione)
  • ++ (operatore di incremento pre)
    • + (operatore più unario)
      • e (operando)

Si noti che non crea un errore di sintassi, ma il " LValue requrired "errore del compilatore.

4

Per aggiungere un'ulteriore svolta alle risposte corrette già fornite qui, se si compila con il flag -s, il compilatore C emetterà un file di assembly in cui possono essere esaminate le istruzioni generate effettivamente. Con il seguente codice C:

int b=1, c=2, d=3, e=4; 
int a = b * (c * d * + e); 

Complesso generato (usando gcc, compilazione per amd64) inizia con:

movl $1, -20(%ebp) 
    movl $2, -16(%ebp) 
    movl $3, -12(%ebp) 
    movl $4, -8(%ebp) 

così possiamo identificare memoria individuale posizioni -20 (% ebp) come variabile b , fino a -8 (% ebp) come variabile e. -4 (% epp) è variabile a.Ora, il calcolo viene reso come:

movl -16(%ebp), %eax 
    imull -12(%ebp), %eax 
    imull -8(%ebp), %eax 
    imull -20(%ebp), %eax 
    movl %eax, -4(%ebp) 

Così, come è stato commentato da altre persone che hanno risposto, il compilatore tratta i semplicemente "+ e" come l'operazione unaria positivo. La prima istruzione movl posiziona il contenuto della variabile e nel registro dell'accumulatore EAX, che viene quindi prontamente moltiplicato per il contenuto della variabile d o -12 (% ebp), ecc.