Newsgroup: it.comp.lang.c
Titolo: Leggere una struttura

Salve, dovrei leggere questa struttura da un file, che rappresenta 
l'header di un file wav:
typedef struct
{
char chunkID[4];     
int ChunkSize;       
char Format[4];     
char Subchunk1ID[4];
int Subchunk1Size;  
short int AudioFormat;  
short int NumChannels;  
int SampleRate;         
int ByteRate;           
short int BlockAlign;                        
short int BitsPerSample;
char Subchunk2ID[4];    
int Subchunk2Size;  
} RIFF_HEADER;
Se leggo la struttura con fread, alla fine non mi trovo con i dati 
esatti, in particolare con le stringhe. Ad esempio, il campo Format viene 
letta assieme al campo Subchunk1ID, però poi il campo Subchunk1ID viene 
letto correttamente, Ecco un esempio. Se faccio eseguire il programma:
./wave_format Recorded.wav
Filename:Recorded.wav
Header Size=44
ChunkID:RIFFNl 7 <-- ERRORE 
ChunkSize:158798
Format:WAVEfmt
Subchunk1ID:fmt
Subchunk1Size:18
AudioFormat:1
NumChannels:2
SampleRate:44100
ByteRate:176400
BlockAlign:4
BitsPerSample:16
Subchunk2ID: <- ERRORE
Subchunk2Size:1814585716  <-- ERRORE
 
Ecco il programma, ancora allo stadio iniziale, cioè un abbozzo.
int main(int argc, char *argv[])
{
RIFF_HEADER *header;
FILE *fp;
char *filename;
if(argc>1)
{
filename=(char *)(malloc(sizeof(argv[1])));
strcpy(filename,argv[1]);
printf("Filename:%s\n",filename);
fp=fopen(filename,"rb");
}
header=(RIFF_HEADER *)malloc(sizeof(RIFF_HEADER));
printf("Header Size=%d\n",sizeof(RIFF_HEADER));
fread(header,sizeof(RIFF_HEADER),1,fp);
printf("ChunkID:%s %d\n",header->chunkID,strlen(header->chunkID));
printf("ChunkSize:%d\n",header->ChunkSize);
printf("Format:%s\n",header->Format);
printf("Subchunk1ID:%s\n", header->Subchunk1ID);
printf("Subchunk1Size:%d\n",header->Subchunk1Size);
printf("AudioFormat:%d\n",header->AudioFormat);
printf("NumChannels:%d\n",header->NumChannels);
printf("SampleRate:%d\n",header->SampleRate);
printf("ByteRate:%d\n",header->ByteRate);
printf("BlockAlign:%d\n",header->BlockAlign);
printf("BitsPerSample:%d\n",header->BitsPerSample);
printf("Subchunk2ID:%s\n",header->Subchunk2ID);
printf("Subchunk2Size:%d\n",header->Subchunk2Size);
return 0;
}
Il problema è che le stringhe in C sono terminate con '\0' e dovrei 
leggere almeno 5 caratteri, ma tale carattere non esiste nel file.
Avreste una soluzione semplice al problema ?
Francesco


no no, così non vai da nessuna parte: ci sono due importanti errori di
fondo.
Come prima cosa, le strutture possono contenere padding tra i suoi
membri, quindi la dimensione della struttura è (può essere) diversa
dalla somma delle dimensioni dei suoi campi.
Seconda cosa, non puoi stampare un buffer di bytes come fossa una
stringa, semplicemente perché, come tu stesso dici, non hai il
terminatore.
Terza cosa (anche se non ci siamo ancora arrivati la scrivo uguale),
per avere un codice portabile dovresti tenere conto dell'endianess del
formato e della tua macchina per i tipi interi, non basta fare una
semplice fread(&ilmioint, sizeof(int), 1, fp).
Quindi:
- Leggi con fread *campo* per *campo* e metti nella struttura
(opzionalmente tenendo conto della endianess).
- Tutti i buffer (che non sono stringhe!) li stampi in output con un
for.
Ciao!

> filename=(char *)(malloc(sizeof(argv[1])));
guarda che questa cosa sopra e' assolutamente sbagliata
e ti puo' portare (se sei fortunato) ad un crash del programma.
sizeof non si puo' usare in quel modo. Rileggiti la definizione.
Tu volevi usare, forse, strlen() e in ogni caso
avresti dovuto aggiungere 1 per il terminatore.
ciao,
g.

 Avreste una soluzione semplice al problema ?
printf("Format:%4.4s\n",header->Format);
E.

 header=(RIFF_HEADER *)malloc(sizeof(RIFF_HEADER));
Anche qui: via il cast.
-- 
questo articolo e` stato inviato via web dal servizio gratuito 
http://www.newsland.it/news
 segnala gli abusi ad 
ab...@newsland.it

 (opzionalmente tenendo conto della endianess).
Non sono del tutto d'accordo con questa soluzione. In fondo il 
linguaggio C e, in particolare, le struct � stato progettato 
appositamente per avere alte prestazioni in questo tipo di operazioni. 
E' possibile specificare per ogni campo di una struct il numero di bit 
da occupare nella struct. In tal modo riesci ad ottenere addirittura 
allineamenti al bit. Non sono sicuro delle garanzie che puoi ottenere 
sull'allineamento risultante ma credo che sia naturale che questo tipo 
di programmi dipendando dall'implementazione. E di certo la 
progettazione dei formati dei files (come i WAV) sono state progettate 
in modo tale da rispettare l'allineamento delle architetture pi� comuni.
E.

 Anche qui: via il cast.
Ok



Questo funziona


eheheh - come si dice... tra il dire e il fare c'è di mezzo il mare.
basterebbe un'estensione del compilatore (estensione che praticamente
*qualsiasi* compilatore ha) per eliminare il padding della struct.
per l'endianess niente da fare - almeno un check all'inizio occorre
farlo: di solito puoi risolvere con una macro sulla struttura finale,
che fa o meno delle operazioni a seconda della macchina.
In ogni caso non sono certo quei cicli macchina in più in lettura che
ti "rovinano" il programma, al massimo ti portano via quei dieci
minuti in più a scrivere dieci righe - poco male, una pausa sigaretta
in meno, tutta salute.
Poi certo - magari di un programma portabile non ti frega nulla e di
sigarette ne fumi due ;-)
Ciao!

 E.
Volendo, ma richiede un po' di lavoro extra.
Per esempio, non puoi usare int, ma devi scegliere int32_t(*), e via 
dicendo; inoltre, come giï¿œ spiegato da chi ha risposto per primo, ci 
sono i problemi di padding e byte-order: ora, la struttura mi pareva non 
avesse (ragionevolmente: aveva blocchi di 4 char, coppie di short e 
interi) bisogno di padding, anche se per evitare problemi un controllino 
non farebbe male(**); per l'ordine invece non si scappa, ti tocca punto, 
altrimenti ti darï¿œ risultati diversi a seconda dell'hardware.
(*) A proposito, tempo fa pozz in un messaggio sull'overflow segnalava 
http://www.embeddedgurus.net/stack-overflow/.
.. ho passato tanto di quel 
tempo a leggerlo che mi sono dimenticato di dire GRAZIE!!!
E btw ha anche una sezione su int32_t e compagnia andante, pensa un po'
(**) nel senso di uno di uno di quei trucchi col preprocessore che 
impediscono la compilazione se non vale un certo vincolo, in questo caso 
che (sizeof(RIFF_HEADER) == (somma delle parti))

 che (sizeof(RIFF_HEADER) == (somma delle parti))
Grazie per la risposta

 sono i problemi di padding e byte-order: 
Il problema del padding, in pratica, non c'ï¿œ. La struttura dati ï¿œ stata 
progettata appositamente per evitare disallineamenti. Il problema del 
byte-order c'ï¿œ... e vï¿œ risolto (se ce n'ï¿œ bisogno) sulle macchine con 
l'endianess diversa.
E.

 da occupare nella struct.
Alcuni compilatori (tipicamente quelli IBM) hanno delle direttive #pragma
specifiche (vedi #pragma pack).



yes. per il gcc invece:
__attribute__((packed))
dopo la struct

 E.
Se non ho sbagliato i calcoli, dovrebbe esserci una differenza tra la 
dimensione della struttura e la somma delle dimensioni di ogni singolo 
membro della struttura. La differenza è di due byte. 
Ora la lettura della struttura funziona, cioè i dati vengono visualizzati 
correttamente, ho dei dubbi solo sull'ultimo valore, un intero molto 
grande.
Ecco il risultato della lettura dell'header
./wave_format Recorded.wav
Lunghezza argv[1]: 12
Filename:Recorded.wav
Header Size=48
Header size=48 fields size=46
Is Padded 0
ChunkID:RIFF
ChunkSize:158798
Format:WAVE
Subchunk1ID:fmt
Subchunk1Size:18
AudioFormat:1
NumChannels:2
SampleRate:44100
ByteRate:176400
BlockAlign:4
BitsPerSample:16
Extra:0
Subchunk2ID:data
Subchunk2Size:4294737921   <-- Numero molto grande
La lettura e la stampa delle informazioni avviene nel seguente modo:
header=(RIFF_HEADER *)malloc(sizeof(RIFF_HEADER));
printf("Header Size=%d\n",sizeof(RIFF_HEADER));
fread(header,sizeof(RIFF_HEADER),1,fp);
printf("Is Padded %d\n",ispadded(header));
printf("ChunkID:%4.4s\n",header->chunkID);
printf("ChunkSize:%d\n",header->ChunkSize);
printf("Format:%4.4s\n",header->Format);
printf("Subchunk1ID:%4.4s\n", header->Subchunk1ID);
printf("Subchunk1Size:%d\n",header->Subchunk1Size);
printf("AudioFormat:%d\n",header->AudioFormat);
printf("NumChannels:%d\n",header->NumChannels);
printf("SampleRate:%d\n",header->SampleRate);
printf("ByteRate:%d\n",header->ByteRate);
printf("BlockAlign:%d\n",header->BlockAlign);
printf("BitsPerSample:%d\n",header->BitsPerSample);
printf("Extra:%d\n",header->Extra);
printf("Subchunk2ID:%4.4s\n",header->Subchunk2ID);
printf("Subchunk2Size:%ld\n",(header->Subchunk2Size)/8);

 E.
Se non ho sbagliato i calcoli, dovrebbe esserci una differenza tra la 
dimensione della struttura e la somma delle dimensioni di ogni singolo 
membro della struttura. La differenza è di due byte. 
Ora la lettura della struttura funziona, cioè i dati vengono visualizzati 
correttamente, ho dei dubbi solo sull'ultimo valore, un intero molto 
grande.
Ecco il risultato della lettura dell'header
./wave_format Recorded.wav
Lunghezza argv[1]: 12
Filename:Recorded.wav
Header Size=48
Header size=48 fields size=46
Is Padded 0
ChunkID:RIFF
ChunkSize:158798
Format:WAVE
Subchunk1ID:fmt
Subchunk1Size:18
AudioFormat:1
NumChannels:2
SampleRate:44100
ByteRate:176400
BlockAlign:4
BitsPerSample:16
Extra:0
Subchunk2ID:data
Subchunk2Size:4294737921   <-- Numero molto grande
La lettura e la stampa delle informazioni avviene nel seguente modo:
header=(RIFF_HEADER *)malloc(sizeof(RIFF_HEADER));
printf("Header Size=%d\n",sizeof(RIFF_HEADER));
fread(header,sizeof(RIFF_HEADER),1,fp);
printf("Is Padded %d\n",ispadded(header));
printf("ChunkID:%4.4s\n",header->chunkID);
printf("ChunkSize:%d\n",header->ChunkSize);
printf("Format:%4.4s\n",header->Format);
printf("Subchunk1ID:%4.4s\n", header->Subchunk1ID);
printf("Subchunk1Size:%d\n",header->Subchunk1Size);
printf("AudioFormat:%d\n",header->AudioFormat);
printf("NumChannels:%d\n",header->NumChannels);
printf("SampleRate:%d\n",header->SampleRate);
printf("ByteRate:%d\n",header->ByteRate);
printf("BlockAlign:%d\n",header->BlockAlign);
printf("BitsPerSample:%d\n",header->BitsPerSample);
printf("Extra:%d\n",header->Extra);
printf("Subchunk2ID:%4.4s\n",header->Subchunk2ID);
printf("Subchunk2Size:%ld\n",(header->Subchunk2Size)/8);


Se, dopo aver letto tutti i consigli, non li segui ma insisti nel fare
gli stessi errori mi sa che non arriverai mai! ;)

 __attribute__((packed))
Da:
http://gcc.gnu.org/onlinedocs/gcc-3.1.1/gcc/Type-Attributes.html
This keyword is followed by an attribute specification inside double 
parentheses.
Perch� si utilizzano le doppie parentesi?



Io ho seguito "quasi" tutti i consigli, ed alla fine sono rimasto con un 
solo errore. Ho voluto insistere con la lettura dell'intera struttura 
perchè mi sembrava il modo più semplice e chiaro. Per il padding, non so 
se questo è il problema, e nemmeno come eliminarlo.


anche per questo hai già avuto risposta: con una direttiva del
compilatore. A seconda di quale usi cerca su manuale/help/internet
come si fa!
Ciao!

 Perché si utilizzano le doppie parentesi?
perché "dentro" al gcc __attribute__ è definito con qualcosa tipo:
#ifndef __GNUC__
#  define  __attribute__(x)  /*NOTHING*/
#endif
la doppia parentesi elimina il problema di compatibilità con altri
compilatori.

Sarchiapone ha scritto:
 > Header size=48 fields size=46
 > [...]
 > printf("Header Size=%d\n",sizeof(RIFF_HEADER));
Sì, ma visto che hai modificato il codice, come facciamo a dirti cosa 
c'è di sbagliato? Nel primo estratto che avevi mostrato venivano 44 
byte, che corrisponde alla struttura che avevi definito.
E.



Ok, ho risolto, scrivendo __attribute__ (( packed )) RIFF_HEADER; dopo la 
struttura. Ma confesso che non avevo nessuna idea di come avrei potuto 
risolvere da solo questo problema. Forse la tua soluzione di leggere un 
campo alla volta dal file era la migliore e più semplice.
La soluzione di affidarsi ad una caratteristica del compilatore non mi 
sembra molto portabile.
Grazie



Ok, ho risolto, scrivendo __attribute__ (( packed )) RIFF_HEADER; dopo la 




Ho dovuto aggiungere un campo di due byte, che esiste nella definizione 
della struttura, e che io avevo ignorato. Ma tale aggiunta mi risolveva 
parzialmente il problema.