Newsgroup: it.comp.lang.c
Titolo: Copiare una struttura in un'altra

A proposito di un mio precedente post dal subject "Mescolare Little e
Big Endian", sto cercando il migliore approccio per copiare una
sequenza di byte (una struttura con campi in Big Endian) in una
struct.
Per esempio, ho 7 byte:
  unsigned char array[7];
con questi dati:
  byte 0: unsigned char
  byte 1-2: unsigned int (16 bit, Big Endian)
  byte 3-4-5-6: unsigned long (32 bit, Big Endian)
  byte 7: unsigned char
Ovviamente non posso definire una struct per problemi di allineamento
(il mio processore è a 16bit), sia per problemi di endianess.
Allora definisco una struct di dimensione diversa:
  struct MyStruct {
    unsigned char x;
    unsigned int y;
    unsigned long z;
    unsigned char w;
  };
Sicuramente sizeof(struct MyStruct) non è uguale a 7.
Qual è il modo più efficiente per copiare i dati dall'array di byte
alla struttura? Per ora faccio così:
  struct MyStruct mys;
  unsigned char array[[7];
  mys.x = array[0];
  mys.y = (array[1] * 0x100u) + array[2];
  mys.z = (array[3] * 0x1000000u) + (array[4] * 0x10000u) + (array[5]
* 0x100u) + array[6];
  mys.w = array[7];


metodo 1:
pack della struttura (#pragma pack, __attribute__((packed)) o simili -
dipende dal compilatore)
metodo 2:
assumo che la struttura sia per "uso interno" mentre il buffer sia per
la "trasmissione".
se non sono disponibili le funzioni htons e htonl , definisci due
funzioni simili (le mie le chiamo le_uintXX (o be_uintXX) e così via,
quando possibile come macro su htons/l)
e due funzioni struct2buf e buf2struct:
la prima, a spanne:
int struct2buf(struct MyStruct *st, unsigned char *buf)
{
    uint16_t tmpi;
    uint32_t tmpl;
    if (st==NULL || buf==NULL)
        return WRONG_PARAMS;
    tmpi = htons((uint16_t)st->y);
    tmpl = htonl((uint32_t)st->z);
    buf[0] = st->x;
    memcpy(buf+1, &tmpi, sizeof(tmpi)); /* o copia byte a byte */
    memcpy(buf+3, &tmpl, sizeof(tmpl)); /* o copia byte a byte */
    buf[7] = st->w;
    return 0;
}
int buf2struct(unsigned char *buf, struct MyStruct *st)
{
    /* uguale ma al contrario :-) */
    return UNIMPLEMENTED;
}
Ciao!

 quando possibile come macro su htons/l)
Mi aiuteresti a definirle? E comunque sarebbero valide per interi 
unsigned. Dovrei definirne altre due per interi signed.

 unsigned. Dovrei definirne altre due per interi signed.
Non e' necessario differenziare per tipo :-)
// Da Big-endian 16 a little endian16
#define ENDIAN_BE16(__x) ((((__x)>>8)&0xff)|((__x)&0xff)<<8))
// Da Big-endian 32 a little endian32
#define ENDIAN_BE32(__x) ((((__x)>>24)&0xff) | \
			 (((__x)>> 8)&0xff00)| \
			 (((__x)&0xff00)<< 8)| \
			 (((__x)&0xff)<< 24))
// Da Big-endian 64 a little endian64
#define ENDIAN_BE64(__x) ((((__x)>>56)& 0xff) | \
			 (((__x)>>40)& 0xff00) | \
			 (((__x)>>24)& 0xff0000) | \
  			 (((__x)>>8)& 0xff000000) | \
			 (((__x)&0xff000000)<<8) | \
			 (((__x)&0xff0000)<< 24) | \
			 (((__x)&0xff00)<< 40) | \
			 (((__x)&0xff)<< 56))
bye
a.


Esatto. Faccio così anch'io (utilizzando funzioni invece di macro,
tanto il compilatore le metterebbe comunque inline).
Se vuoi sfruttare il fatto che la conversione è solo per copia, puoi
anche direttamente convertire copiando:
void endian_be16(uint16_t i, unsigned char *buf)
{
    *buf = (i>>8)&0xff;
    *(buf+1) = i & 0xff;
}
etc.
Anche se IMO è molto meno elegante.
Ciao!