Saya membaca bahwa urutan bidang bit dalam sebuah struct adalah khusus platform. Bagaimana jika saya menggunakan opsi pengemasan khusus kompiler yang berbeda, apakah data jaminan ini akan disimpan dalam urutan yang benar seperti yang tertulis? Sebagai contoh:
struct Message
{
unsigned int version : 3;
unsigned int type : 1;
unsigned int id : 5;
unsigned int data : 6;
} __attribute__ ((__packed__));
Pada prosesor Intel dengan kompiler GCC, bidang diletakkan di memori seperti yang ditunjukkan. Message.version
adalah 3 bit pertama dalam buffer, dan Message.type
diikuti. Jika saya menemukan opsi pengemasan struct yang setara untuk berbagai kompiler, apakah ini akan menjadi lintas platform?
c++
c
bit-manipulation
endianness
bit
dewald.dll
sumber
sumber
Jawaban:
Tidak, ini tidak akan sepenuhnya portabel. Opsi pengepakan untuk struct adalah ekstensi, dan tidak sepenuhnya portabel. Selain itu, C99 §6.7.2.1, paragraf 10 mengatakan: "Urutan alokasi bidang bit dalam sebuah unit (urutan tinggi ke urutan rendah atau urutan rendah ke urutan tinggi) ditentukan oleh implementasi."
Bahkan kompiler tunggal mungkin meletakkan bidang bit secara berbeda tergantung pada endianness platform target, misalnya.
sumber
packed
memberlakukan pemesanan: stackoverflow.com/questions/1756811/… cara menerapkan urutan bit: stackoverflow.com/questions/6728218/gcc-compiler-bit-orderBidang bit sangat bervariasi dari kompiler ke kompiler, maaf.
Dengan GCC, mesin big endian meletakkan bit big end terlebih dahulu dan mesin little endian meletakkan bit little end terlebih dahulu.
K&R mengatakan "Anggota bidang [bit-] yang berdekatan dari struktur dikemas ke dalam unit penyimpanan yang bergantung pada implementasi dalam arah yang bergantung pada penerapan. Ketika bidang yang mengikuti bidang lain tidak akan muat ... itu mungkin dibagi antara unit atau unit mungkin empuk. Bidang tanpa nama dengan lebar 0 memaksa bantalan ini ... "
Oleh karena itu, jika Anda memerlukan tata letak biner independen mesin, Anda harus melakukannya sendiri.
Pernyataan terakhir ini juga berlaku untuk non-bitfields karena padding - namun semua kompiler tampaknya memiliki beberapa cara untuk memaksa pengemasan byte dari suatu struktur, seperti yang saya lihat Anda sudah temukan untuk GCC.
sumber
Bitfield harus dihindari - mereka tidak terlalu portabel antar kompiler bahkan untuk platform yang sama. dari standar C99 6.7.2.1/10 - "Penentu struktur dan gabungan" (ada kata-kata yang mirip dalam standar C90):
Anda tidak dapat menjamin apakah bidang bit akan 'menjangkau' batas int atau tidak dan Anda tidak dapat menentukan apakah bidang bit dimulai di ujung bawah int atau ujung atas int (ini terlepas dari apakah prosesornya big-endian atau little-endian).
Lebih suka bitmask. Gunakan sebaris (atau bahkan makro) untuk mengatur, menghapus dan menguji bit.
sumber
_BIT_FIELDS_LTOH
dan_BIT_FIELDS_HTOL
endianness berbicara tentang urutan byte bukan urutan bit. Saat ini , 99% yakin bahwa pesanan bit sudah diperbaiki. Namun, saat menggunakan bitfield, ketangguhan harus diperhitungkan. Lihat contoh di bawah ini.
#include <stdio.h> typedef struct tagT{ int a:4; int b:4; int c:8; int d:16; }T; int main() { char data[]={0x12,0x34,0x56,0x78}; T *t = (T*)data; printf("a =0x%x\n" ,t->a); printf("b =0x%x\n" ,t->b); printf("c =0x%x\n" ,t->c); printf("d =0x%x\n" ,t->d); return 0; } //- big endian : mips24k-linux-gcc (GCC) 4.2.3 - big endian a =0x1 b =0x2 c =0x34 d =0x5678 1 2 3 4 5 6 7 8 \_/ \_/ \_____/ \_____________/ a b c d // - little endian : gcc (Ubuntu 4.3.2-1ubuntu11) 4.3.2 a =0x2 b =0x1 c =0x34 d =0x7856 7 8 5 6 3 4 1 2 \_____________/ \_____/ \_/ \_/ d c b a
sumber
Sebagian besar waktu, mungkin, tapi jangan bertaruh pada pertanian itu, karena jika Anda salah, Anda akan rugi besar.
Jika Anda benar-benar perlu memiliki informasi biner yang identik, Anda perlu membuat bitfields dengan bitmask - misalnya Anda menggunakan unsigned short (16 bit) untuk Message, dan kemudian membuat sesuatu seperti versionMask = 0xE000 untuk mewakili tiga bit teratas.
Ada masalah serupa dengan penyelarasan dalam struct. Misalnya, Sparc, PowerPC, dan 680x0 CPU semuanya big-endian, dan default umum untuk kompiler Sparc dan PowerPC adalah menyelaraskan anggota struct pada batas 4-byte. Namun, satu kompiler yang saya gunakan untuk 680x0 hanya selaras pada batas 2-byte - dan tidak ada opsi untuk mengubah perataan!
Jadi untuk beberapa struct, ukuran pada Sparc dan PowerPC adalah identik, tetapi lebih kecil pada 680x0, dan beberapa anggota berada di offset memori yang berbeda di dalam struct.
Ini adalah masalah dengan satu proyek yang saya kerjakan, karena proses server yang berjalan di Sparc akan meminta klien dan mengetahui bahwa itu adalah big-endian, dan menganggap itu hanya dapat menyemprotkan struct biner di jaringan dan klien dapat mengatasinya. Dan itu bekerja dengan baik pada klien PowerPC, dan crash besar-besaran pada klien 680x0. Saya tidak menulis kodenya, dan butuh waktu cukup lama untuk menemukan masalahnya. Tapi mudah untuk diperbaiki begitu saya melakukannya.
sumber
Terima kasih @BenVoigt atas awal komentar Anda yang sangat berguna
Sumber Linux memang menggunakan bit field untuk mencocokkan dengan struktur eksternal: /usr/include/linux/ip.h memiliki kode ini untuk byte pertama dari datagram IP
struct iphdr { #if defined(__LITTLE_ENDIAN_BITFIELD) __u8 ihl:4, version:4; #elif defined (__BIG_ENDIAN_BITFIELD) __u8 version:4, ihl:4; #else #error "Please fix <asm/byteorder.h>" #endif
Namun mengingat komentar Anda, saya menyerah mencoba membuat ini berfungsi untuk bidang bit multi-byte frag_off .
sumber
Tentu saja jawaban terbaik adalah dengan menggunakan kelas yang membaca / menulis bidang bit sebagai aliran. Menggunakan struktur bidang bit C tidak dijamin. Belum lagi dianggap tidak profesional / malas / bodoh untuk menggunakan coding ini di dunia nyata.
sumber