Reinterpretazione dati portatile

Voglio reinterpretare i dati di un tipo come un altro tipo in modo portatile (C99). Non sto parlando di casting, voglio una reinterpretazione di alcuni dati dati. Inoltre, per portatile intendo che non infrange le regole C99 – non intendo dire che il valore reinterpretato sia uguale su tutti i sistemi.

Conosco 3 modi diversi di reinterpretare i dati, ma solo due di questi sono portatili:

  1. Questo non è portabile: infrange la rigida regola dell’aliasing.

    /* #1 Type Punning */ float float_value = 3.14; int *int_pointer = (int *)&float_value; int int_value = *int_pointer; 
  2. Questo dipende dalla piattaforma, perché legge un valore int dall’unione dopo aver scritto un float in esso. Ma non infrange le regole C99, quindi dovrebbe funzionare (se sizeof(int) == sizeof(float) ).

     /* #2 Union Punning */ union data { float float_value; int int_value; }; union data data_value; data_value.float_value = 3.14; int int_value = data_value.int_value; 
  3. Dovrebbe andare bene, a patto di sizeof(int) == sizeof(float)

     /* #3 Copying */ float float_value = 3.14; int int_value = 0; memcpy(&int_value, &float_value, sizeof(int_value)); 

Le mie domande:

  1. È corretto?
  2. Conoscete altri modi per reinterpretare i dati in modo portatile ?

La soluzione 2 è portabile: il tipo di punteggiatura dei sindacati è sempre stato legale in C99 ed è stato reso esplicito con TC3, che ha aggiunto la seguente nota alla sezione 6.5.2.3:

Se il membro utilizzato per accedere ai contenuti di un object union non è lo stesso del membro utilizzato per archiviare un valore nell’object, la parte appropriata della rappresentazione dell’object del valore viene reinterpretata come una rappresentazione dell’object nel nuovo tipo come descritto in 6.2.6 (un processo a volte chiamato “tipo punning”). Questa potrebbe essere una rappresentazione trappola.

L’allegato J lo elenca ancora come comportamento non specificato, che è un difetto noto ed è stato corretto con C11, che è cambiato

Il valore di un membro del sindacato diverso da quello memorizzato in [ non specificato ]

a

I valori dei byte che corrispondono ai membri del sindacato diversi da quello memorizzato per ultimi in [ non sono specificati ]

Non è un grosso problema dato che l’allegato è solo informativo, non normativo.

Tieni presente che puoi ancora finire con un comportamento indefinito, ad es

  • creando una rappresentazione trap
  • violando le regole di aliasing in caso di membri con un tipo di puntatore (che non dovrebbe essere comunque convertito tramite punzonatura tipo perché non è necessaria una rappresentazione puntatore uniforms)
  • se i membri del sindacato hanno dimensioni diverse, solo i byte dell’ultimo membro utilizzati in un negozio hanno un valore specificato; in particolare, la memorizzazione di valori in un membro più piccolo può anche invalidare i byte finali di un membro più grande
  • se un membro contiene byte di riempimento, che prendono sempre valori non specificati
  1. La soluzione unione è definita come memcpy one in C (AFAIK, è UB in C ++), vedi DR283

  2. È ansible lanciare un puntatore a un puntatore al carattere (firmato / non firmato /), quindi

     unsigned char *ptr = (unsigned char*)&floatVar; 

    e quindi l’accesso a ptr [0] a ptr [sizeof (floatVar) -1] è legale.

per essere sicuro, andrei con un array di byte (char senza segno) piuttosto che un ‘int’ per contenere il valore.

il tipo di dati int è un esempio di tipo non portatile poiché endianness può cambiare l’ordine dei byte tra le piattaforms.

se vuoi essere portabile devi definire i tuoi tipi, quindi implementarli su ogni piattaforma a cui vuoi effettuare il porting. Quindi definisci i metodi di conversione per i tuoi tipi di dati. Questo è per quanto ne so l’unico modo per avere il pieno controllo degli ordini di byte, ecc.

Se si desidera evitare la regola di aliasing rigida, è necessario prima eseguire il cast in un puntatore char:

 float float_value = 3.14; int *int_pointer = (int *)(char *)&float_value; int int_value = *int_pointer; 

Nota comunque che potresti avere sizeof(int) > sizeof(float) , nel qual caso ottieni comunque un comportamento indefinito