2015-03-20 11 views
6

Si consideri il seguente codice:Utilizzare memcpy per copiare un int in un array di caratteri e quindi stamparne i membri: comportamento non definito?

int i = 1; 
char c[sizeof (i)]; 
memcpy(c, &i, sizeof (i)); 
cout << static_cast<int>(c[0]); 

Si prega di ignorare se questo è buon codice. So che l'output dipende dalla endianità del sistema. Questa è solo una domanda accademica.

È questo codice:

  • comportamento indefinito
  • implementazione definita comportamento
  • comportamento ben definiti
  • Qualcos'altro
+0

è http://stackoverflow.com/q/12612488/560648 non è sufficiente? –

+1

Leggi [basic.types]/p2. 'c' manterrà la rappresentazione dell'oggetto di' i', il cui valore è determinato dall'ordine dei byte di sistema. Il valore di 'c [0]' è * indeterminante * ma il programma è ben formato. – 0x499602D2

+0

@LightnessRacesinOrbit Scusa ma non capisco la rilevanza di tale domanda. –

risposta

3

La regola che si sta cercando è 3.9p4:

La rappresentazione oggetto di un oggetto di tipo T è la sequenza di Nunsigned char oggetti presi dalla oggetto di tipo T, dove N uguale sizeof(T). La rappresentazione del valore di un oggetto è l'insieme di bit che contiene il valore di tipo T. Per i tipi banalmente copiabili, la rappresentazione del valore è un insieme di bit nella rappresentazione dell'oggetto che determina un valore, che è un elemento discreto di un insieme di valori definito dall'implementazione.

Quindi, se si utilizza unsigned char, è possibile prendere un comportamento definito dall'implementazione (qualsiasi implementazione conforme deve dare una garanzia su ciò che il comportamento è).

La lettura tramite char è legale, ma i valori non sono specificati. Tuttavia, si garantisce che l'utilizzo di un valore non qualificato char conserverà il valore (pertanto il valore di char non può avere rappresentazioni di trap o bit di riempimento), in base a 3.9p2:

Per qualsiasi oggetto (diverso da un sotto-oggetto classe base) di tipo banalmente copiabile T, se l'oggetto contiene un valore valido di tipo T, i byte sottostanti (1.7) che costituiscono l'oggetto può essere copiati in un array di char o unsigned char. Se il contenuto dell'array di char o unsigned char viene ricopiato nell'oggetto, l'oggetto deve successivamente conservare il suo valore originale.

(valori "non specificato" sono un po 'più debole di valori "di implementazione definiti" - la semantica sono gli stessi, ma la piattaforma non è tenuto a documentare quali sono i valori.)

+0

"ma poi i valori non sono specificati" - dove lo dice? –

+0

"Pertanto il bare char non può avere rappresentazioni di trap o bit di riempimento" - che non segue la citazione di 3.9p2. Potrebbero esserci rappresentazioni di trap ma non possono essere prodotte tramite tale copia perché l'oggetto originale sarebbe stato una trappola se ce l'avesse. (ad esempio bit di parità) –

+0

char è autorizzato ad aliasare QUALSIASI tipo senza causare un trap, quindi nessun contenuto legale della memoria è una trappola con char. Ciò non esclude ECC, ma i bit ECC non sono "padding". –

6

La lingua non dice che questa operazione è comportamento immediatamente indefinito. Si dice semplicemente che la rappresentazione di c[0]potrebbe essere fino a essere una rappresentazione non valida (trap), nel qual caso il comportamento è effettivamente indefinito. Ma nei casi in cui c[0] non è una rappresentazione trap, il comportamento è definito dall'implementazione.

Se si utilizza l'array unsigned char, la rappresentazione trap diventa impossibile e il comportamento diventa puramente definito dall'implementazione.

+0

Una rappresentazione trap sarebbe possibile se 'i' fosse' unsigned'? –

+2

@Neil Kirk: No. 'unsigned char' non ha rappresentazioni di trap. – AnT

+0

Intendevo 'unsigned int' su' i', ma stai dicendo che 'i' è' signed int' e l'array se 'unsigned char', non ci sarebbe alcuna rappresentazione trap? –

1

È chiaramente un comportamento definito dall'implementazione.

La rappresentazione interna di un int non è definita dallo standard (le implementazioni possono scegliere little o big endian o qualsiasi altra cosa), quindi non può essere un comportamento ben definito: il risultato può essere diverso su diverse architetture.

Su un sistema definito (architettura e compilatore C e (eventualmente) configurazione) il comportamento è perfettamente determinato: su un big endian, otterrete un 1, su un piccolo endian a 0. Quindi è comportamento definito dall'implementazione.

Problemi correlati