Newsgroup: it.hobby.elettronica.digitale
Titolo: ANSI C: accesso ai dati di una union

Salve, sto creando una union per accedere comodamente ai singoli bit/
byte di un long:
union mnglng {
  /* bit access */
  struct Bits {
    unsigned char bit0:1;
    unsigned char bit1:1;
    unsigned char bit2:1;
    unsigned char bit3:1;
    unsigned char bit4:1;
    unsigned char bit5:1;
    unsigned char bit6:1;
    unsigned char bit7:1;
  } byte1, byte2, byte3, byte4;
  unsigned long long1;
} uAddr ;
In questo modo riesco ad accedere ai singoli bit:
uAddr.byte1.bit0 = 0; ad esempio va bene ...
Il problema è che non riesco ad accedere al BYTE :
uAddr.byte1 = 45;
ad esempio mi da errore!
dove sbaglio?
grazie mille!

 grazie mille!

 un intero e che quindi non puoi assegnagli tout-court un valore intero
E' vero .. il problema è proprio quello!
Ho risolto come hai detto tu, dichiarando anche 4 byte  (vedi in fondo
al messaggio)
Il risultato mi piace poco, perchè per accedere ai bit devo fare:
uAddr.byte1.bit0 = 1;
mentre per accedere ai byte devo fare:
uAddr.B1 = 45 ;
quando mi piacerebbe scrivere:
uAddr.byte1 = 45;
come potrei fare? forse dovrei mettere una union al posto della struct
dei bit .. ? boh ?!?
grazie per l'aiuto!
struttura:
union mnglng {
  /* bit access */
  struct Bits {
    unsigned char bit0:1;
    unsigned char bit1:1;
    unsigned char bit2:1;
    unsigned char bit3:1;
    unsigned char bit4:1;
    unsigned char bit5:1;
    unsigned char bit6:1;
    unsigned char bit7:1;
  } byte1, byte2, byte3, byte4;
  /* byte access */
  unsigned char B1;
  unsigned char B2;
  unsigned char B3;
  unsigned char B4;
  /* long access */
  unsigned long long1;
  } uAddr,

 uAddr.byte1 = 45;
Per accedere al byte fai    uAddr.byte1 = 45;
Per accedere al bit 0 della variabile byte1 fai   uAddr.byte1.0 = 1;
La union quindi rimane questa :
union mnglng {
  /* byte access */
  unsigned char byte1;
  unsigned char byte2;
  unsigned char byte3;
  unsigned char byte4;
  /* long access */
  unsigned long long1;
  } uAddr;
Ciao
CG 

 
il massimo che puoi arrivare ad avere usando i costrutti STANDARD del C 
e' una "struttura" del tipo
uAddr.L = 100               per accedere al long nella sua interezza
uAddr.B0.byte = 45          per accedere al byte
uAddr.B0.bits.bit1 = 1      per accedere al bit del byte
( con B0 che puo' essere anche B1 .. B3 e bit0 che puo' essere anche 
bit1 .. bit7 )
questo usando 2 ( due ) livelli di union, la prima che riunisce 8 bit in 
un byte
la seconda che riunisce 4 delle suddette union in un long
chiaramente a meno di 'estensioni' del compilatore


 strutture (hai usato 8 byte per fare 8 booleani!!!!).
Ciao, grazie della risposta, ma con i miei compilatori purtroppo non
funziona :-(
Sto usando Hitech PICC-18 c compiler 8.35pl2 e anche Hitachi embedded
workshop (vecchio .. del 1999 con compilatore IAR vers. 1.41)
Ho usato 8 byte nella struct perchè tra l'altro non posso mettere i
bit all'interno della struttura, mi da errore ..
(cmq ho bvisto che anche la stessa IAR ha fatto le strutture per
l'accesso ai registri del micro in questo modo).

 l'accesso ai registri del micro in questo modo).
Uhm se ricordo bene Hitech accetta i boolean ?
Comunque puoi fare come ti ha suggerito mmm :
union mnglng {
  /* byte access */
  union MyByteAccess {
  boolean bit1, bit2, bit3, bit4, bit5, bit6, bit7, bit8;
  unsigned char B;
  } byte1, byte2, byte3, byte4;



Aggiungo che sebbene la cosa che vuoi fare sia molto diffusa tra i 
programmatori, essa è *proibita* dallo standard ISO e te lo sconsiglio 
vivamente.
Questo per diversi motivi:
1 - Non ti è dato sapere come vengono rappresentati i bitfield, 
dipende dal compilatore. Quello che è certo è che nella pratica se 
dichiari un bitfield di N bit si comporterà in tutte le operazioni 
matematiche come lungo N bit (in particolare sarà giusto l'overflow) 
ma non puoi contare sul fatto che occuperà davvero la posizione e lo 
spazio che pensi tu.
Quindi è buona regola evitare i bitfield come la peste, perché possono 
essere non portabili addirittura tra opzioni di compilazione diverse 
della stessa versione dello stesso compilatore. Non sono fatti per 
essere usati in questo modo e il fatto che le cose ora funzionino non 
significa che lo faranno per sempre.
2 - Le union non sono fatte per scrivere da un campo e rileggere da un 
altro. Lo standard ISO dice che le union sono pensate per avere più 
tipi di dato che condividono la stessa area di memoria e usare 
*alternativamente* uno dei campi, ma non tutti insieme contemporaneamente.
Questo significa che puoi rileggere da un campo della union solo dopo 
che hai scritto in quello specifico campo. Se scrivi in un campo e 
rileggi da un altro il risultato è indefinito.
Il compilatore assume quanto detto dallo standard per cercare di 
ottimizzare e per questo non è detto che la union sia allocata come 
pensi tu.
Cerca di evitare queste porcate perché distruggono la portabilità e la 
compatibilità del tuo codice, anche sullo stesso compilatore! Per 
assurdo può succedere che tutto sembra funzionare in debug senza 
ottimizzazioni, ma appena le attivi o le cambi, il tuo codice può 
esplodere in maniera casuale!
Lo so, purtroppo queste pratiche sono diffuse, e si vede tanto codice 
scritto in questo modo perché magari si crede di avere più efficienza 
ed eleganza.
Però è bene educarsi a *non* usarle e se proprio lo si vuole fare 
(perché si è *strasicuri* che il compilatore che usiamo lo permette) 
bisogna essere consci dei rischi.
Ciao
-- 
  _|/ Francesco Sacchi - Develer S.r.l., R&D dept.
   |\ 
http://www.develer.com/
 - 
http://www.bertos.org

 essere consci dei rischi.
Ci sono delle situazioni però in cui, anche per un briciolo di amor proprio, 
non puoi fare a meno di usarle!
Se devi passare 8 bit alla volta (per esempio tramite uart lenta) un 
centinaio di float che hai opportunamente bufferizzato (es. un ADC da 
megasamples/s che ha acquisito un transitorio) ti conviene farlo da una 
union!
Uno dei motivi perchè privilegio il C è perchè ho pieno controllo su queste 
cose.
Tant'è vero che in Basic o Pascal non esistono
Ciao
CG 

 bisogna essere consci dei rischi.
Ti propongo un'alternativa per accedere ai singoli bit e ai singoli 
byte di un dato:
/* Ritorna lo stato di bit in data */
#define GET_BIT(data, bit) ((data) & (1 << (bit)))
/* Setta il bit in data */
#define SET_BIT(data, bit) ((data) |= (1 << (bit)))
/* Resetta il bit in data */
#define RESET_BIT(data, bit) ((data) &= ~(1 << (bit)))
/* Ritorna il byte indicato, prendendolo da data (byte va da 0 a 3) */
#define GET_BYTE(data, byte) (((data) >> ((byte) * 8)) & 0xff)
/* Setta il byte di data a value */
#define SET_BYTE(data, byte, value) \
do { \
	(data) &= ~(0xff << ((byte) * 8)); \
	(data) |= ((value) << ((byte) * 8)); \
} while (0)
Le ho scritte al volo e non le ho testate, ma penso che con qualche 
aggiustatina dovrebbero funzionare!


 cose.
Puoi avere lo stesso il controllo senza però ricorrere alle porcate.


 bisogna essere consci dei rischi.
Ciao Francesco, grazie della risposta!
Sono daccordo con te che questa pratica è poco portabile,
purtroppo ...
Io sono dell'avviso che uno dei vantaggi del linguaggio C è anche
l'uso di dati strutturati, e non vorrei privarmene!
Per dirti, se in un futuro dovessi passare dal C al C++  (visto che i
micro continuano ad aumentare le performance),
almeno arrivo con una mentalità già preparata!
Nel mio caso devo scrivere in i2c un dato ad una eprom.
L'indirizzo ha 18bit, e quindi lo metto in un long.
Quando lo scrivo nell'i2c i bit dell'indirizzo vengono messi nei 3
byte da spedire, e fra i vari bit si intromettono dei bit che dicono
ad esempio se devo leggere/scrivere ecc.. quindi pensavo di fare la
struttura con la union, leggere il dato come union, e poi elaborarlo
con accesso ai singoli bit.
Questo lo posso fare anche con le funzioni che mi hai consigliato, ad
esempio:
uAddr.byte1.bit5 = uAddr.byte1.bit4;
diventerebbe
if (GET_BIT(byte1, 5))  SET_BIT(byte1, 4)

>   } uAddr,
la struttura è concettualmente errata in quanto i campi B1 e B4 sono 
esattamente gli
stessi, cosi' come la struttua a bit, viso che e' definita nella union.
struct bit8{
       unsigned char bit0:1
       ........
       unsigned char bit7:1
}BIT8_T;
struct bit32{
       unsigned long bit0:1
       ........
       unsigned long bit31:1
}BIT32_T;
union p{
    BIT8_T     bits[4];
    BIT32_T   bitl;
    unsigned char b8[4];
    unsigned long b32;
}x;
// accesso  a bit:
x.bits[0].bit1=........;
x.bits[1].bit1=........;
x.bits[2].bit1=........;
x.bits[3].bit1=........;
// Accesso abit (32)
x.bitl.bit0=....;
............
x.bitl.bit31=....;
// accesso a byte:
x.b8[0]= ........;
x.b8[1]= ........;
bye

 almeno arrivo con una mentalità già preparata!
Sono d'accordo che sia giusto saper usare i dati strutturati, ma saperli 
usare non significa metterli dappertutto anche quando non è il caso!
Visto che l'oggetto del post inizia con "ANSI C", il modo in cui stai 
cercando di usare le union non è per niente ANSI.
Come dici, forse in futuro passerai ad altri compilatori e linguaggi, e 
queste sono errori che è bene evitare fin da subito. Il codice che hai 
riportato è errato secondo lo standard e non è portabile, se il tuo 
scopo è imparare ti sconsiglio vivamente di usarlo anche se il 
compilatore attuale si comporta come ti aspetti.
E per concludere, non credere che usare queste porcate aumenti le 
performance: per accedere ai singoli bit il compilatore deve lo stesso 
andare di shift e operatori logici. Su piattaforme con più di 8bit ci 
possono poi essere problemi di allineamento che rendono l'accesso al 
singolo byte in una struttura addirittura più lento rispetto alla 
larghezza della parola della CPU.
Nel tuo caso il modo più efficiente secondo me è semplicemente andare di 
operatori logici per formare i byte da inviare alla eeprom.
-- 
  _|/ Francesco Sacchi - Develer S.r.l., R&D dept.
   |\ 
http://www.develer.com/
 - 
http://www.bertos.org/

> pensi tu.
Non mi è chiaro cosa significa questo passaggio!
In pratica, se io ho una union che contiene 1 byte ed 8 bit (quindi in
memoria occupa 1 byte ...) ,
vorrei scrivere il dato accedendo come byte, per poi gestirne i
singoli bit accedendo a livello di bit.
Questo non è quindi giusto concettualmente? E non funziona?
In realtà nel mio caso scrivo 1 long e cerco di accedere ai 4 byte, e
la cosa sembra non funzionare ..
Non so se sbaglio in altri punti del programma, ma mi è tornato in
mente questo passaggio che hai scritto e mi è venuto quindi un dubbio!
Comunque sto seguendo il tuo consiglio!
In questo caso lascio perdere le union e uso le funzioni che hai
gentilmente pubblicato qui,
grazie mille

 grazie mille
Di nulla, fammi sapere se vanno, le ho scritte al volo!
-- 
  _|/ Francesco Sacchi - Develer S.r.l., R&D dept.
   |\ 
http://www.develer.com/
 - 
http://www.bertos.org/

 float e così via.
Mi inserisco...
Questo mi è chiaro e lo trovo molto bello. Però non capisco come mai 
dichiarando come constante una union non si possa inizializzare un campo 
successivo al primo. Cioè, si potrebbe anche ma bisogna che il 
compilatore sia C99 compliant se ho ben inteso i suggerimenti dell'amico 
'pozz'.
Cioè, non mi spiego la difficoltà nell'assegnare il valore a un campo 
piuttosto che a un altro... mi sembra una limitazione troppo pesante.
Ma ovviamente avranno le loro ragioni, spero :(
Marco / iw2nzm

 aggiustatina dovrebbero funzionare!
Funzionano bene, le sto usando tutte, tranne SET_BYTE
grazie Francesco!

 la cosa sembra non funzionare ..
Se ti interessa proprio questo caso non puoi semplicemente indirizzare 
il byte che ti interessa come unsigned char?
Cioè:
long int a; unsigned char *c;
c=(unsigned char *)&a; a=13;
printf("\n\nindirizzo di a %u",&a);
printf("\nvalore di c %u",c);
printf("\nvalore di c[0] %d",*(c+1));
printf("\nvalore di c[1] %d",*(c+1));
printf("\nvalore di c[2] %d",c[2]);
printf("\nvalore di c[3] %d",c[3]);
a me viene "valore di c[3] 13" e gli altri 0 perché sono su ppc.

 
ho la sensazione di aver fatto qualche errore da queste parti, mi sa che 
ci vuole un altro "livello" di nomi per accedere ai 4 gruppi di 
byte/bits se uno li vuole allineati (sovrapposti) col long


Marco Trapanese ha scritto:

Per le strutture non ci sono limitazioni, se hai una struttura fatta così:
struct foo
{
	int a;
	int b;
	int c;
};
Per inizializzarla, con un compilatore non C99 devi fare così:
1):
struct foo bar = {0, 1, 2};
Se il compilatore è C99 puoi invece scrivere, più comodamente:
2):
struct foo bar =
{
	.a = 0,
	.b = 1,
	.c = 2,
};
Lo stesso vale per le union, ma i campi delle union condividono lo 
stesso spazio e quindi non ha senso assegnarne più di uno.
Puoi assegnare come inizializzatore ad una union un solo valore, e dato 
che con un compilatore non-C99 puoi solo usare la sintassi 1) è stato 
stabilito che quel valore viene assegnato al primo campo.
Esempio
union jack
{
	int a;
	float b;
}
Caso non-C99, puoi solo fare:
union jack bar = {0}; // Viene messo a 0 il campo a della union.
Col C99 puoi fare:
union jack bar = { .b = 3.2 }; // Viene messo a 3.2 il campo b della union.
Purtroppo con compilatori non-C99 non ci sono possibilità, l'estensione 
per il C99 è stata fatta apposta per questi casi qui.


 a me viene "valore di c[3] 13" e gli altri 0 perché sono su ppc.
Infatti, lo stesso codice sulla mia piattaforma(Athlon 64) da 13 alla 
posizione 0.
-- 
  _|/ Francesco Sacchi - Develer S.r.l., R&D dept.
   |\ 
http://www.develer.com/
 - 
http://www.bertos.org/

 posizione 0.
Almeno il risultato è quello che ci aspettavamo :-)

 di conoscere l'endianess e ovviamente di tenerne conto.
Portabile significa che lo stesso codice, senza cambiamenti o casi 
particolari funziona su tutte le piattaforme.
Se devi vedere su che piattaforma sei per interpretare i risultati 
questo è proprio l'opposto della portabilità: devo fare cose diverse in 
base a dove mi trovo.
Viceversa la soluzione con gli shift è portabile perché funziona sempre 
e comunque.


 aggiustatina dovrebbero funzionare!
Ti propongo un altro problema. Devi accedere ad un byte (o ad una 
variabile a 32 bit) bit a bit. In altre parole, il byte sarà:
   abcd efgh
dove a rappresenta qualcosa
      b rappresenta un'altra cosa
      ...
      h rappresenta un'altra cosa ancora
Potrei fare così:
---
struct {
    unsigned char a:1;
    unsigned char b:1;
    unsigned char c:1;
    unsigned char d:1;
    unsigned char e:1;
    unsigned char f:1;
    unsigned char g:1;
    unsigned char h:1;
} byte;
---
E potrei accedere ai vari bit in modo semplice (per settare solo a e b):
---
*(unsigned char *)byte = 0x00;
byte.a = 1;
byte.b = 1;
---
Questa soluzione è sicuramente compilabile e portabile su qualsiasi 
altra piattaforma. Quello che non è sicuro è che l'ordine dei campi sia 
giusto, per esempio che il primo bitfield dichiarato sia proprio l'MSBit 
oppure l'LSBit. Ma alla fine non è importante, il tutto sarà coerente 
con l'architettura.
Il problema nasce quando devi trasferire questi dati ad un'altra 
architettura che, magari, ha la necessità di avere i bit nel giusto 
ordine (penso alle varie periferiche seriali che ragionano in termini di 
byte e bit).
Volendo utilizzare le tue macro, dovrei fare qualcosa del genere:
---
byte = 0x00;
SET_BIT (byte,7);
SET_BIT (byte,6);
---
Questo sicuramente funziona su tutte le piattaforme, però ho perso una 
cosa molto comoda, cioè accedere ai vari bit col loro nome mnemonico 
piuttosto che con il loro numero.
Certo è che potrei sempre fare:
---
#define A_BIT 7
#define B_BIT 6
...
#define H_BIT 0
...
byte = 0x00;
SET_BIT (byte,A_BIT);
SET_BIT (byte,B_BIT);
---

 #define B_BIT 6
Sì, un altro modo comodo è avere un enum, che è molto più leggero da 
scrivere e da leggere:
enum
{
	a_bit = 0,
	b_bit,
	c_bit,
	...
};
Se magari descrivi meglio il problema che vuoi risolvere possiamo 
trovare macro carine ad hoc che mantengono l'eleganza e la semplicità 
di lettura pur non usando bitfield.


 particolari funziona su tutte le piattaforme.
Capisco, intendi una portabilità semnatica, io mi limitavo a intendere 
che il codice fosse compilato senza errori passando da una piattaforma 
all'altra.


Bhe anche il codice con le union e bitfield si compila su tutti i 
compilatori e piattaforme, solo che i risultati sono diversi.
A meno di non usare estensioni specifiche del compilatore, in genere, 
tutto il codice si compila senza errori, e qui che infatti nasce il 
problema.
Scrivere codice che fa la stessa cosa su architetture e compilatori 
diversi è meno banale, è questo il senso di "portabilità".
Almeno è così che si intende nell'informatica negli ultimi anni.


 Puoi avere lo stesso il controllo senza però ricorrere alle porcate.
Io ritengo che le union siano una figata, altro che una porcata. Sicuramente 
il mio parere non è chissà come autorevole, ma la penso così
Ciao
CG