2012-01-30 10 views
5

Guardate questo codice di esempio C (estratto un test come esempio):non comprendere il comportamento operatore di spostamento nel codice C

main() { 
    unsigned long a, b; 
    int c; 
    c = 32; 
    a = 0xffffffff << 32; 
    b = 0xffffffff << c; 
    printf ("a=%x, b=%x\n", a, b); 
} 

stampe: a=0, b=ffffffff

non riesco a capire perché b non è zero, proprio come a. Ho provato questo su Microsoft C e GCC.

Aggiornamento: ho fissato l'errore di battitura stupido (che avrebbe dovuto essere < < C e non < < b ovviamente). Ma la mia domanda rimane valida, ad es. il risultato è sempre lo stesso.

risposta

3

In C è un comportamento indefinito verso sinistra spostare più della larghezza dell'operando promosso.

(C99, 6.5.7p3) "Se il valore dell'operando di destra è negativo o è maggiore o uguale alla larghezza dell'operando di sinistra promosso, il comportamento non è definito."

presumo negli esempi seguenti int e unsigned int tipi sono 32 bit

Il tipo di un intero esadecimale senza suffisso costante è il primo nell'elenco corrispondente in cui il suo valore può essere rappresentato:. int , unsigned int, long, unsigned long, long long, unsigned long long.

Così qui 0xFFFFFFFF è di tipo unsigned int e 0xFFFFFFFF << 32 è un comportamento indefinito.

+0

Downvoter, potresti spiegare perché hai downvoted? – ouah

+0

Offri una buona quantità di dettagli per la parte a della domanda, ma nulla per spiegare la parte b. – BitBank

+1

@BitBank perché la parte 'a' e la parte' b' è lo stesso problema (lo stesso spostamento a sinistra viene eseguito) con la stessa spiegazione. Entrambe le espressioni nel RHS dell'assegnazione di 'a' e' b' sono comportamenti indefiniti. Poiché è un comportamento indefinito, il risultato di 'a' e' b' può essere lo stesso o può essere diverso o il programma può 'rm -rf' la partizione di root. Naturalmente, possiamo osservare differenti euristiche di compilazione perché in questo primo caso viene utilizzata una costante come operando corretto del turno, ma ciò è irrilevante. – ouah

6

Non si può mai inizializzato b a nulla prima di usarlo qui:

b = 0xffffffff << b; 

in modo che possa essere qualsiasi cosa. (Credo che in realtà intendeva spostare da c.)


A parte questo:

Il problema principale è che lo spostamento dal # di bit nel tipo di dati o più è comportamento indefinito.
Quindi, se il letterale 0xffffffff è un intero a 32 bit, quindi la linea:

a = 0xffffffff << 32; 

non è definito dalla norma. (Vedere i commenti.)


Si dovrebbe anche essere sempre un avviso del compilatore:

warning C4293: '<<' : shift count negative or too big, undefined behavior 
+0

perché * così se non firmato è solo 32 bit *? se 'unsigned int' è 32-bit allora' 0xffffffff << 32' è un comportamento non definito. – ouah

+0

@ouah, ah sì buon punto. Fixing ... – Mysticial

1

Hai b = 0xffffffff << b;, ma forse volevi dire b = 0xffffffff << c;

0

Perché dovrebbe essere pari a zero? Si spostarlo da un numero spazzatura (il primo valore di b) che può essere 0.

0

Lei non è stato inizializzato B, quindi sta andando ad avere qualsiasi valore (casuale) è in quel particolare locazione di memoria al momento il programma funziona.

0

Quando si raggiunge la linea b = 0xffffffff << b;, il valore di b non è stato assegnato. Sembra che il suo valore sia zero, rendendo l'istruzione b = 0xffffffff << b;. Quale espelle la risposta data.

0

Le buone pratiche sono AND dopo il turno. In questo caso sai bene cosa succede.

examply:

b << = 4; 
b &= 0xfffffff0; 
2

Scommetto che dopo aver corretto il tuo errore di battitura si ottengono 2 zeri come risultato. Ecco perché: Se il tuo programma è (scommetto) compilato come rilascio, hai attivato le ottimizzazioni. Ciò significa che il compilatore userà il cosiddetto "folding costante" e la CPU non eseguirà nemmeno questi spostamenti. Sotto il codice del cofano ci saranno 2 costanti di zero inserite nello stack e inviterà printf. I risultati di queste operazioni (turni) diventeranno essenzialmente costanti nel programma. Quindi non ci sono comportamenti indefiniti, ecc - la vostra A e B diventa valori costanti ed otterrete STH come:

push 0 
push 0 
push offset to printf format string 
call to printf 
+0

Beh, ovviamente questo codice è solo un esempio che ho messo insieme per analizzare un problema in un codice di produzione, ma hai ragione: con 'gcc -O3' ottengo due zeri, e con' gcc -O0' ottengo 0 e 0xffffffff . – haimg

+0

Quindi in modalità di debug (nessuna ottimizzazione) si ottiene nel primo caso 0 (il compilatore lo rende costante quindi 0). Il secondo caso mostra che stai utilizzando CPU x86 in cui il numero di bit da spostare a sinistra è mascherato (con 31), quindi in sostanza si sposta sempre a sinistra (operando << (count & 0x1F)) e quindi (0xFFFFFFFF << 0) porta a invariato ' b'. (È possibile verificare che in "Manuale di sviluppo del software Intel Architecture Volume 2: Riferimento del set di istruzioni" (materiale molto utile) – Artur

2

Nessuno ha menzionato un altro aspetto interessante del problema. Sui computer x86, la quantità di spostamento viene usata come modulo 32, quindi uno spostamento di 32 sarà effettivamente uno spostamento di 0. Sulle macchine ARM, il valore di spostamento (se proveniente da un registro) non viene alterato, quindi qualsiasi valore di spostamento di 32 o maggiore risulterà sempre 0 nel registro di destinazione.

In particolare nel tuo problema, il compilatore è intelligente riguardo ai valori costanti, quindi trasforma (0xffffffff < < 32) nel valore corretto (0). Nel tuo secondo esempio, il compilatore non può calcolare direttamente l'importo dello spostamento poiché proviene da una variabile. Se eseguito su una macchina Intel, lo spostamento verso sinistra di 32 risulta in uno spostamento a sinistra di 0.

+0

Questo è completamente irrilevante –

+0

Questa è la risposta corretta. – xiaokaoy

Problemi correlati