Sì, anche se spesso sono considerazioni che alla fin della fiera derivano da come è fatto l'hardware. Per dire, il padding inserito nelle strutture in genere deriva da vincoli architetturali (per cui, ad esempio, accedere a certi tipi di dati è molto lento o addirittura non supportato se non sono allineati in memoria in una certa maniera), e in genere è roba che tutti i compilatori implementano alla stessa maniera su una data architettura.
Sui bitfield ci sono due ulteriori arbitrarietà che non vengono dettate in maniera "ovvia" dall'architettura;
- non è completamente scontata la dimensione delle word da usare per indirizzare la memoria che sta "dietro" il bitfield;
- è arbitrario l'ordine in cui vengono messi i campi all'interno delle word in questione - si possono riempire da sinistra a destra come da destra a sinistra;
- non è scontato cosa fare se un campo del bitfield finisce "a cavallo" tra una word e l'altra - si può fare la cosa efficiente in termini di spazio (tenere metà bit da una parte, metà dall'altra) o quella più efficiente in termini di performance (metterli tutti nella seconda word)
Esempio:
codice:
struct A {
int a: 5;
int b: 10;
int c: 16;
int d: 15;
};
Immaginiamo di essere su una macchina a 32 bit: potremmo usare 2 word da 32 bit e impaccare tutto allineando "a destra":
codice:
DWORD 1 | DWORD 2
31 0 | 31 0
dccccccccccccccccbbbbbbbbbbaaaaa __________________dddddddddddddd
Oppure si potrebbe impaccare a sinistra:
codice:
DWORD 1 DWORD 2
31 0 | 31 0
aaaaabbbbbbbbbbccccccccccccccccd dddddddddddddd__________________
Oppure si potrebbe evitare di stare inutilmente stretti, e lasciare un bit di padding sulla prima DWORD per evitare di fare due accessi separati e un milione di shift inutili per cavare fuori d
codice:
DWORD 1 | DWORD 2
31 0 | 31 0
_ccccccccccccccccbbbbbbbbbbaaaaa _________________ddddddddddddddd
Oppure passare a word da 16 bit e allineare tutto in maniera più intelligente, occupando meno spazio e ottenendo performance migliori (le letture e le scritture su c e su d diventano letture/scritture "normali" a 16 bit invece di necessitare di un mucchio di shift):
codice:
WORD 1 WORD 2 WORD 3
15 0 | 15 0 | 15 0
_bbbbbbbbbbaaaaa | cccccccccccccccc | _ddddddddddddddd
Quindi insomma, sui bitfield c'è molta arbitrarietà, e il compilatore avrebbe abbastanza spazio di manovra per fare un po' quel che gli pare e ottimizzare al meglio.
Ora, in realtà su piattaforme "moderne" di solito è ben definita una ABI di qualche genere che specifica esattamente questo tipo di dettagli di basso livello - dalle convenzioni di chiamata (in quali registri vengono passati i parametri, chi è responsabile della pulizia dello stack dopo la chiamata, quali registri sono preservati, ...) al layout delle struct C; questo è necessario altrimenti non sarebbe possibile l'interazione anche solo tra programmi e sistema operativo.
Le ABI in genere specificano anche come devono essere gestiti tutti questi casini dei bitfield, per cui, data una determinata piattaforma (per dire Linux su x86_64), la sua ABI (nel caso in questione, la ABI System V adattata per x86_64) e preso un compilatore che aderisce alla ABI (gcc, clang, icc), tutti questi dovrebbero alla fine produrre la stessa rappresentazione per un bitfield specificato alla stessa maniera (a patto di stare nelle regole specificate dalla ABI).
Ciò detto, tutto questo è assolutamente irrilevante visto che in 10 anni di C e C++ credo di aver visto usare i bitfields "per davvero" forse due volte.