PARTE 2/2 (causa limiti del forum)
Ora che abbiamo un'infarinatura generale vediamo cosa ha combinato @astrobeed:
typedef struct { // Frame
char Chr1; //1 byte
char Chr2; //1 byte
char Chr3; //1 byte
char Chr4; //1 byte
char Arr[4]; // 4 byte
unsigned long Val1; // 4byte
unsigned long Val2; // 4byte
} Frame; //16 byte
è stata creata una struttura solida, non portabile ma non ne è stata spiegata bene, ecco perchè del mio "punzecchiamento".
Perchè questa struttura è abbastanza solida?
Perchè quella struttura su qualsiasi compilatore(odierno) microsoft restituirà 16 byte dunque è compatibile con la dichiarazione del chip a 8 bit che restituisce sempre 16byte.
Ma non può essere modificata senza rispettare alcune regole elencate sempre nel link in maniera dettagliata, pena il non corretto funzionamento del codice!
e l'unione?
union Gest_Dati { // union che gestisce il buffer
Frame Dati; //16 byte
char Buffer[16]; //16 byte
} Dati_U;
l'unione in questo caso non fa niente dato che frame ha la stessa dimensione di buffer l'unione creerà una varibile di 16 byte, perchè ha usato l'unione? semplicemente perchè è una tecnica di sile, tale unione rende il codice piu leggibile e dunque è piu difficile fare errori.
ora dobbiamo verificare che quello appena detto sia tutto vero, iniziamo dalla cosa piu semplice, l'unione è solo uno stile?
typedef struct { // Frame
char Chr1; //1 byte
char Chr2; //1 byte
char Chr3; //1 byte
char Chr4; //1 byte
char Arr[4]; // 4 byte
unsigned long Val1; // 4byte
unsigned long Val2; // 4byte
} Frame; //16 byte
union Gest_Dati { // union che gestisce il buffer
Frame Dati; //16 byte
char Buffer[16]; //16 byte
};
int main()
{
union Gest_Dati un;
int i;
//Style
for ( i = 0; i < sizeof(union Gest_Dati); ++i )
un.Buffer[i] = 1;
printf("%d %d %d %d %d %d %d %d %d %d\n",un.Dati.Chr1,un.Dati.Chr2,un.Dati.Chr3,un.Dati.Chr4,
un.Dati.Arr[0],un.Dati.Arr[1],un.Dati.Arr[2],un.Dati.Arr[3],
un.Dati.Val1,un.Dati.Val2);
//VS obfuscated C
char* p;
for ( p = (char*)&un.Dati, i = 0; i < sizeof(union Gest_Dati); ++i )
*p++ = 0;
printf("%d %d %d %d %d %d %d %d %d %d\n",un.Dati.Chr1,un.Dati.Chr2,un.Dati.Chr3,un.Dati.Chr4,
un.Dati.Arr[0],un.Dati.Arr[1],un.Dati.Arr[2],un.Dati.Arr[3],
un.Dati.Val1,un.Dati.Val2);
return 0;
}
Settiamo dunque a 1 usando il metodo della unione e visualizziamo che ralmente sia avvenuto tutto ciò e verifichiamo che accedendo direttamente alla struttura eseguiamo la stessa identica cosa settando tutto a 0 e visualizzando.
Ora verifichiamo la reale instabilità della tecnica mal spiegata, creiamo un nostro frame che debba contenere un valore char e un valore int, vogliamo inviare il valore ad una arduino uno, una due e al pc con windows a 32bit.
Su arduino uno: teorico == reale grazie all'architettura 8bit
typedef struct { // Frame
char Chr1; //1 byte
int Val1; //2byte
} Frame; //3 byte
union Gest_Dati { // union che gestisce il buffer
Frame Dati; //3 byte
char Buffer[3]; //3 byte
};// 3byte
Su arduino due/PC: teorico != reale causa all'architettura 32bit
Terorico:
typedef struct { // Frame
char Chr1; //1 byte
int Val1; //4byte
} Frame; //5 byte
union Gest_Dati { // union che gestisce il buffer
Frame Dati; //5 byte
char Buffer[3]; //5 byte
};// 5byte
l'instabilità dell'uso classico dei tipi del c ci ha già creato dei problemi, la struttura non è piu compatibile con quella della uno!, nonostante il buffer sia di 3 elementi l'unione risulterà di 5 byte teorici.
in piu la cigliegina nasce da come il compilatore modifica il tutto:
typedef struct { // Frame
char Chr1; //1 byte
char pad[3] //3 byte
int Val1; //4byte
} Frame; //8 byte
union Gest_Dati { // union che gestisce il buffer
Frame Dati; //8 byte
char Buffer[3]; //3 byte
};// 8byte
e qui nasce un casino! infatti il compilatore interviene ad aggiungere lo spazio per il padding subito dopo la variabile Chr1
allineando la struttura da 5byte a 8! si capisce bene che si la struttura di @Astrobeed è solida ma solo se non viene toccata e la si usa cosi comè su architetture a 32bit o 64 con compilatori microsoft, l'uso dell'unione in questo caso poi non ci aiuta anzi potrebbe erroneamente sviarci.
Ecco perchè ho consigliato di inviare i dati semplicemente in maniera estesa, perchè non bisogna tenere conto di ciò che un compilatore può fare e di ciò che un'architettura richiede, semplificando e rafforzando il nostro codice.
Nota finale l'accesso alla struttura sia tramite puntatore sia tramite union richiede lo stesso numero di clock, l'invio in modalità estesa o l'invio diretto di una struttura richiede lo stesso numero di clock.
Cosa significa modalità estesa?
significa inviare membro per membro in modalità byte, dunque nessuna conversione.
Spero di essermi riuscito a spiegare nonostante il discorso sia lungo, complesso e molto articolato per essere trattato su di un forum.(per di piu con il limite forse "ridicolo" di 9000 caratteri)
Ops mi sono dimenticato di parlare dell'endianness, a quanto pare @astrobeed non era solo quello il problema......