2013-09-28 21 views
13

Perché questo programma fornisce numeri imprevisti (es: 2040866504, -786655336)?Operatore ternario in C

#include <stdio.h> 
int main() 
{ 
    int test = 0; 
    float fvalue = 3.111f; 
    printf("%d", test? fvalue : 0); 

    return 0; 
} 

Perché sta stampando numeri imprevisti invece di 0? dovrebbe dovrebbe fare un typecast implicito? Questo programma è per scopi didattici niente di serio.

+0

Che cosa intendi esattamente per "casuale"? Stai cercando di stampare un numero in virgola mobile come numero intero, ma quel numero dovrebbe essere '0' che ha la stessa rappresentazione di bit in entrambi i tipi. –

+1

Casuale o solo errato? –

+0

Questa domanda sembra essere fuori tema perché riguarda chi sa cosa. –

risposta

18

Molto probabilmente, la piattaforma passa valori in virgola mobile in un registro in virgola mobile e valori interi in un registro diverso (o sullo stack). Hai detto a printf di cercare un numero intero, quindi sta guardando nel registro che gli interi vengono passati (o in pila). Ma l'hai passato a float, quindi lo zero è stato inserito nel registro a virgola mobile che printf non ha mai guardato.

L'operatore ternario segue le regole del linguaggio per decidere il tipo di risultato. A volte non può essere un numero intero e qualche volta essere un galleggiante. Quelli potrebbero essere di dimensioni diverse, memorizzati in luoghi diversi e così via, il che renderebbe impossibile generare codice sano per gestire entrambi i possibili tipi di risultati.

Questa è una supposizione. Forse sta accadendo qualcosa di completamente diverso. Il comportamento non definito non è definito per un motivo. Questo tipo di cose può essere impossibile da prevedere e molto difficile da capire senza molta esperienza e conoscenza dei dettagli della piattaforma e del compilatore. Non permettere mai a qualcuno di convincerti che UB è ok o sicuro perché sembra funzionare sul loro sistema.

+0

L'unica cosa che manca da questa risposta è il motivo per cui il parametro di punto in primo luogo galleggiava. –

+0

Ho aggiunto qualche altra informazione al riguardo nel paragrafo centrale. –

+0

wow ... ottima spiegazione !! Ora il concetto è chiaro :-) – Tonmoy

11

Poiché si utilizza %d per la stampa di un valore float. Utilizzare %f. L'utilizzo di %d per stampare un valore float richiama il comportamento non definito .


EDIT: Per quanto riguarda i commenti di OP;

Perché sta stampando numeri casuali invece di 0?

Quando si compila questo codice, il compilatore dovrebbe darvi un avvertimento:

[Warning] format '%d' expects argument of type 'int', but argument 2 has type 'double' [-Wformat] 

Questo avviso si spiega da sé che questa riga di codice invoca un comportamento non definito. Questo perché, la specifica di conversione %d specifica che printf deve convertire un valore int da binario in una stringa di cifre decimali, mentre %f fa lo stesso per un valore float. Al passaggio il compilatore fvalue sa che è del tipo float ma d'altra parte vede che printf si aspetta un argomento di tipo int. In questi casi, a volte fa ciò che ti aspetti, a volte fa quello che mi aspetto. A volte fa quello che nessuno si aspetta (Nice Comment David Schwartz).
Vedere i casi di test 1 e 2. Funziona perfettamente con %f.

dovrebbe supporre di eseguire typecast implicito?

No.

+0

Vero, ma non penso che importi con il codice specifico mostrato qui. –

+0

Perché sta stampando numeri casuali invece di 0? dovrebbe dovrebbe fare un typecast implicito? – Tonmoy

+4

@MarkRansom; Perché? Vedi i casi di test [1] (http://ideone.com/Ig5uub) e [2] (http://ideone.com/woe4XU). – haccks

8

Anche se le risposte upvoted esistenti sono corrette, penso che sono troppo tecnici e ignorare la logica di un programmatore principiante potrebbe avere:

Guardiamo la dichiarazione causando confusione in alcune teste:

printf("%d", test? fvalue : 0); 
     ^^ ^ ^
     | |  |  | 
     | |  |  - the value we expect, an integral constant, hooray! 
     | |  - a float value, this won't be printed as the test doesn't evaluate to true 
     | - an integral value of 0, will evaluate to false 
     - We will print an integer! 

Ciò che il compilatore vede è un po 'diverso. È d'accordo sul valore di test che significa false. È d'accordo sul fvalue di essere un float e 0 un numero intero. Tuttavia, ha appreso che i diversi esiti possibili dell'operatore ternario devono essere dello stesso tipo! int e float no. In questo caso, "float vince", 0 diventa 0.0f!

Ora printf non è sicuro. Ciò significa che puoi falsamente dire "stampami un numero intero" e passare un float senza che il compilatore se ne accorga. È successo esattamente Indipendentemente dal valore di test, il compilatore ha dedotto che il risultato sarà di tipo float. Quindi, il tuo codice è equivalente a:

float x = 0.0f; 
printf("%d", x); 

A questo punto, si verifica un comportamento non definito. float semplicemente non è una cosa integral cosa è previsto da %d.

Il comportamento osservato dipende dal compilatore e dalla macchina che si sta utilizzando. Si possono vedere elefanti danzanti, anche se la maggior parte dei terminali non supporta questo afaik.

1

Quando abbiamo l'espressione E1 ? E2 : E3, sono coinvolti quattro tipi. Le espressioni E1, E2 e E3 hanno ciascuna un tipo (ei tipi di E2 e E3 possono essere diversi). Inoltre, l'intera espressione E1 ? E2 : E3 ha un tipo.

Se E2 e E3 hanno lo stesso tipo, allora questo è semplice: l'espressione generale ha quel tipo. Siamo in grado di esprimere questo in una meta-notazione in questo modo:

(T1 ? T2 : T2) -> T2 
"The type of a ternary expression whose alterantives are both of the same type T2 
is just T2." 

Se non hanno lo stesso tipo, le cose si fanno un po 'interessante, e la situazione è molto simile a E2 e E3 essere coinvolti insieme in un aritmetica operazione. Ad esempio, se si aggiunge unoe float, l'operando int viene convertito in float. Questo è ciò che sta accadendo nel tuo programma. La situazione tipo è:

(int ? float : int) -> float 

il test fallisce, e quindi il valore 0int viene convertito nel valore 0.0float.

Questo valore float non è compatibile con lo specificatore %d conversione di printf, che richiede un int.

Più precisamente, il valore float subisce un altro. Quando un float viene passato come uno degli argomenti trascinamento a una funzione variadic, viene convertito in double.

Quindi il valore double 0.0 viene passato a printf dove si aspetta int.

In ogni caso, è un comportamento non definito: è un codice non portatile per il quale la definizione standard ISO del linguaggio C non offre un significato.

Da qui in poi, possiamo applicare un ragionamento specifico della piattaforma perché non vediamo semplicemente 0. Supponiamo che int sia un tipo a 32 bit e quattro byte e che double sia la rappresentazione comune a 64 bit, 8 byte, IEE754 e che venga utilizzato un valore a zero bit per 0.0. Quindi, perché non una parte a 32 bit di questo all-bit-zero viene trattata da printf come valore 0 ?

È possibile che il valore dell'argomento double a 64 bit imponga l'allineamento di 8 byte quando viene inserito nello stack, eventualmente spostando il puntatore dello stack di quattro byte. E quindi printf estrae i dati obsoleti da questi quattro byte anziché i bit zero dal valore double.