Newsgroup: it.comp.lang.c
Titolo: struttura con dimensione errata

Salve a tutti!!!
Ho un problema: devo leggere da un file binario 264 byte, che vorrei
mettere in una struttura. Quando definisco questa struttura, pero', la
dimensione della struttura stessa restituita da sizeof è di 266,
nonostante la somma di tutti i byte che compongono la struttura è di 264.
Dov'è l'errore? sto utilizzando visual studio 6.
Grazie a tutti
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 Dov'è l'errore? sto utilizzando visual studio 6.
probabilmente e' un problema di allineamento:
ovvero i compilatori per velocizzare i trasferimenti dalla memoria
ai registri della cpu (piu' o meno...) di solito "allineano" le
variabili rispetto a multipli del byte, tipo ai 2, 4 o anche 8 byte.
Chiaramente se l'allineamento e' a 4 byte e le variabili sono
degli interi (ormai tipicamente a 4 byte) o dei double (tipicamente ad 
8) non ci perdi nulla.
Se pero' la tua variabile era uno short int da 2 byte o 1 char da 1 byte
chiaramente perdi qualcosa, che di solito e' abbastanza irrilevante.
(La stessa cosa potrebbe accadere con i double da 10 byte quando 
l'allineamento e' ai 4 byte).
Se pero' stai impacchettando vari tipi in una struct e hai 10 milioni
di struct allora magari anche i singoli byte possono servire.
Per modificare il comportamento del compilatore riguardo 
all'allineamento ci sono di solito delle opzioni di compilazione che 
riguardano le variabil in generale, e poi per quel che riguarda le
struct di solito sono ammesse delle direttive da mettere nel codice
(tipo #pragma, credo) per
forzare un certo allineamento solo di certe strutture (per evitare
che il resto del codice faccia schifo).
non conosco il tuo compilatore (per il quale potrai avere migliori 
risposte su it.comp.programmare.win32 o qualcosa di simile) ma se
cerchi nell'help in linea "allineamento" dovresti trovare qualcosa.
ciao,
g.

 Dov'è l'errore? sto utilizzando visual studio 6.
la causa potrebbe essere "struct member alignment" che di solito e' settato 
a 4, puoi provare a settarlo ad 1.... MA:
Ad ogni modo se anche questo risolvesse il tuo problema non e' davvero una 
soluzione portabile.
Non e' detto che tutti i compilatori ti offrano la possibilita' di settare 
l'alignment ed inoltre il codice sorgente in se (senza settare parametri) 
non garantisce che sia portabile.
Inoltre c'e' un discorso di big-endian/little-endian. Ovvero se il file 
binario con 264 bytes contiene dati in formato big-endian di cui alcuni sono 
ints (ad esempio) e tu li leggi in una struttura su di una macchina 
little-endian essi conterrano valori totalmente diversi. Ancora una volta la 
tua soluzione non e' affatto portabile.
Inoltre ancora, c'e' un discorso di tipi: se tu ora usi "int a; int b;" e 
sai che essi sono 8 bytes, questo stesso programma compilato su di un'altra 
macchina potrebbe usare un numero diverso di bytes e quindi il modo in cui 
leggi i 264 bytes sarebbe totalmente errato.
etc....
Insomma non e' davvero questo il modo di procedere, almeno che tu non sia al 
100% sicuro che userai sempre lo stesso compilatore, lo stesso progetto 
(settings), il tutto sulla stessa macchina e sullo stesso sistema operativo, 
etc... A volte anche se sei al 100% sicuro di questa cosa le cose cambiano 
con il tempo ed partire con un software che e' subito legato in questo modo 
non e' il modo migliore di partire!
Scendendo nei dettagli di member struct alignment... assumendo che int sia 4 
bytes e short 2 bytes:
int a;
short b;
short c;
con struct member alignment 4 avra' probabilmente un sizeof() di 8.
short b;
int a;
short c;
con struct member alignment 4 avra' probabilmente sizeof() di 12.
un ottimo esempio e':
char c1;
int i1;
char c2;
int i2;
char c3;
int i3;
che occupa 24 bytes mentre:
int i1;
int i2;
int i3;
char c1;
char c2;
char c3;
occupa 16 bytes;
mettendo l'alignment ad 1 entrambe occuperanno 15 bytes.
Tommaso

 Inoltre c'e' un discorso di big-endian/little-endian. [...]
Per ovviare a tutti i problemi descritti da Tommaso, il modo migliore è
definirsi delle funzioncine per leggere BYTE-PER-BYTE ognuno dei tipi di
dato fondamentali richiesti. Esempio:
   #include <stdio.h>
   #include <stdbool.h>
   bool readByte (FILE * f, unsigned char * pb)
   {
      return fread(pb, 1, 1, f) == 1;
   }
   bool writeByte (FILE * f, unsigned char b)
   {
      return fwrite(&b, 1, 1, f) == 1;
   }
   bool readWord (FILE * f, unsigned short * pw)
   {
      unsigned char b1, b2;
      if (fread(&b1, 1, 1, f) == 1 &&
          fread(&b2, 1, 1, f) == 1)
      {
         *pw = (unsigned short)
               ((unsigned short)(b1 & 0xFF) << 8 |
                (unsigned short)(b2 & 0xFF));
         return true;
      }
      return false;
   }
   bool writeWord (FILE * f, unsigned short w)
   {
      unsigned char b1, b2;
      b1 = (unsigned char)((w >> 8) & 0xFF);
      b2 = (unsigned char)(w & 0xFF);
      return fwrite(&b1, 1, 1, f) == 1 &&
             fwrite(&b2, 1, 1, f) == 1;
   }
   bool readDword (FILE * f, unsigned long * pd)
   {
      unsigned char b1, b2, b3, b4;
      if (fread(&b1, 1, 1, f) == 1 &&
          fread(&b2, 1, 1, f) == 1 &&
          fread(&b3, 1, 1, f) == 1 &&
          fread(&b4, 1, 1, f) == 1)
      {
         *pd = (unsigned long)(b1 & 0xFF) << 24 |
               (unsigned long)(b2 & 0xFF) << 16 |
               (unsigned long)(b3 & 0xFF) << 8  |
               (unsigned long)(b4 & 0xFF);
         return true;
      }
      return false;
   }
   bool writeDword (FILE * f, unsigned long d)
   {
      unsigned char b1, b2, b3, b4;
      b1 = (unsigned char)((d >> 24) & 0xFF);
      b2 = (unsigned char)((d >> 16) & 0xFF);
      b3 = (unsigned char)((d >> 8) & 0xFF);
      b4 = (unsigned char)(d & 0xFF);
      return fwrite(&b1, 1, 1, f) == 1 &&
             fwrite(&b2, 1, 1, f) == 1 &&
             fwrite(&b3, 1, 1, f) == 1 &&
             fwrite(&b4, 1, 1, f) == 1;
   }
In questo modo è tutto sotto il tuo controllo, quale che sia la dimensione
dei dati e la "endianness" dell'ambiente specifico. Cambiando compilatore
o processore, le funzioni rimangono valide.
È importante ricordare che il file deve sempre essere aperto come file
BINARIO, cioè bisogna mettera una lettera "b" come secondo carattere del
secondo argomento della fopen():
     FILE * fileInput = fopen("pippo", "rb");
     FILE * fileOutput = fopen("peppo", "wb");
Non ti preoccupare troppo del fatto che leggere byte per byte possa
rallentare il programma: le funzioni della libreria <stdio.h> hanno il
loro buffering interno, quindi in realtà non accedo al disco byte per byte
ma per "blocchi" della dimensione più opportuna per il sistema operativo
su cui girano.
--
Cingar
