Ketika pemrograman dalam CI telah menemukan itu sangat berharga untuk mengemas struct menggunakan __attribute__((__packed__))
atribut GCCs sehingga saya dapat dengan mudah mengkonversi potongan terstruktur dari memori volatile ke array byte yang akan ditransmisikan melalui bus, disimpan ke penyimpanan atau diterapkan ke blok register. Packs struct menjamin bahwa ketika diperlakukan sebagai array byte tidak akan mengandung padding, yang keduanya boros, risiko keamanan yang mungkin dan mungkin tidak kompatibel ketika dengan interfacing perangkat keras.
Apakah tidak ada standar untuk packing struct yang berfungsi di semua kompiler C? Jika tidak maka saya seorang outlier dalam berpikir ini adalah fitur penting untuk pemrograman sistem? Apakah pengguna awal bahasa C tidak menemukan kebutuhan untuk mengemas struct atau apakah ada beberapa alternatif?
sumber
Jawaban:
Dalam sebuah struct, yang penting adalah offset dari masing-masing anggota dari alamat setiap instance struct. Tidak terlalu banyak masalah seberapa ketat hal-hal yang dikemas.
Sebuah array, bagaimanapun, penting dalam bagaimana "dikemas". Aturan dalam C adalah bahwa setiap elemen array persis N byte dari sebelumnya, di mana N adalah jumlah byte yang digunakan untuk menyimpan tipe itu.
Tetapi dengan struct, tidak ada kebutuhan untuk keseragaman.
Inilah salah satu contoh skema pengemasan yang aneh:
Freescale (yang membuat mikrokontroler otomotif) membuat mikro yang memiliki co-prosesor Time Processing Unit (google for eTPU atau TPU). Ini memiliki dua ukuran data asli, 8 bit dan 24 bit, dan hanya berkaitan dengan bilangan bulat.
Struct ini:
akan melihat setiap U24 menyimpan blok 32 bitnya sendiri, tetapi hanya di area alamat tertinggi.
Ini:
akan memiliki dua U24 disimpan di blok 32 bit yang berdekatan, dan U8 akan disimpan dalam "lubang" di depan U24 pertama
elementA
,.Tetapi Anda dapat memberitahu kompiler untuk mengemas semuanya ke dalam blok 32 bitnya sendiri, jika Anda mau; ini lebih mahal pada RAM tetapi menggunakan lebih sedikit instruksi untuk mengakses.
"packing" tidak berarti "packing dengan ketat" - itu hanya berarti beberapa skema untuk mengatur elemen struct menggunakan offset.
Tidak ada skema generik, itu tergantung pada kompiler + arsitektur.
sumber
struct b
untuk bergerakelementC
sebelum elemen lainnya, maka itu bukan kompiler C yang sesuai. Penataan ulang elemen tidak diizinkan di CKarena Anda menyebutkan
__attribute__((__packed__))
, saya menganggap niat Anda adalah untuk menghilangkan semua padding dalam astruct
(membuat setiap anggota memiliki keselarasan 1-byte).... Dan jawabannya adalah tidak". Padding dan penyelarasan data relatif terhadap sebuah struct (dan array berdekatan dari struct dalam stack atau heap) ada karena alasan penting. Pada banyak mesin, akses memori yang tidak selaras dapat menyebabkan potensi penalti kinerja yang signifikan (meskipun menjadi kurang pada beberapa perangkat keras yang lebih baru). Dalam beberapa skenario kasus yang jarang terjadi, akses memori yang tidak selaras menyebabkan kesalahan bus yang tidak dapat dipulihkan (bahkan dapat merusak seluruh sistem operasi).
Karena standar C difokuskan pada portabilitas, tidak masuk akal untuk memiliki cara standar untuk menghilangkan semua lapisan dalam struktur dan hanya membiarkan bidang yang sewenang-wenang diselaraskan, karena dengan melakukan hal itu berpotensi berisiko membuat kode C non-portabel.
Cara teraman dan paling portabel untuk mengeluarkan data seperti itu ke sumber eksternal dengan cara yang menghilangkan semua lapisan adalah dengan membuat serial ke / dari byte stream alih-alih hanya mencoba mengirim isi memori mentah Anda
structs
. Itu juga mencegah program Anda dari menderita hukuman kinerja di luar konteks serialisasi ini, dan juga akan memungkinkan Anda untuk menambahkan bidang baru ke bebasstruct
tanpa membuang dan merusak seluruh perangkat lunak. Ini juga akan memberi Anda ruang untuk mengatasi endianness dan hal-hal seperti itu jika itu menjadi masalah.Ada satu cara untuk menghilangkan semua padding tanpa mencapai arahan khusus compiler, meskipun itu hanya berlaku jika urutan relatif antara bidang tidak masalah. Diberikan sesuatu seperti ini:
... kita membutuhkan padding untuk akses memori yang disejajarkan relatif ke alamat struktur yang berisi bidang-bidang ini, seperti:
... di mana
.
menunjukkan padding. Setiap orangx
harus menyelaraskan ke batas 8-byte untuk kinerja (dan kadang-kadang bahkan memperbaiki perilaku).Anda dapat menghilangkan padding dengan cara portabel dengan menggunakan representasi SoA (structure of array) seperti itu (mari kita asumsikan kita membutuhkan 8
Foo
instance):Kami telah secara efektif menghancurkan struktur. Dalam hal ini, representasi memori menjadi seperti ini:
... dan ini:
... tidak ada lagi padding overhead, dan tanpa melibatkan akses memori yang tidak selaras karena kita tidak lagi mengakses bidang data ini sebagai penyeimbang alamat struktur, melainkan sebagai penyeimbang alamat dasar untuk apa yang secara efektif sebuah array.
Ini juga membawa bonus menjadi lebih cepat untuk akses berurutan sebagai akibat dari lebih sedikit data untuk dikonsumsi (tidak ada padding yang tidak relevan dalam campuran untuk memperlambat laju konsumsi data yang relevan dengan mesin) dan juga potensi bagi kompiler untuk melakukan vectorisasi pemrosesan dengan sangat sepele. .
The downside adalah bahwa itu adalah PITA ke kode. Ini juga berpotensi kurang efisien untuk akses acak dengan langkah lebih besar di antara bidang, di mana sering repetisi AoS atau AoSoA akan melakukan lebih baik. Tapi itu satu cara standar untuk menghilangkan bantalan dan mengemas barang sekencang mungkin tanpa mengacaukan segalanya.
sumber
Tidak semua arsitektur sama, cukup aktifkan opsi 32 bit pada satu modul, dan lihat apa yang terjadi ketika menggunakan kode sumber yang sama dan kompiler yang sama. Pesanan Byte adalah batasan terkenal lainnya. Lemparkan dalam representasi floating point dan masalahnya menjadi lebih buruk. Menggunakan Packing untuk mengirim data biner adalah non-portabel. Untuk membakukannya sehingga bisa digunakan secara praktis, Anda perlu mendefinisikan ulang spesifikasi Bahasa C.
Meskipun umum, menggunakan Pack untuk mengirim data biner adalah ide yang buruk jika Anda menginginkan keamanan, portabilitas atau umur panjang data. Seberapa sering Anda membaca gumpalan biner dari sumber ke dalam program Anda. Seberapa sering Anda memeriksa semua nilai yang waras, bahwa peretas atau perubahan program belum 'mendapatkan' data? Pada saat Anda mengkodekan rutin pemeriksaan, Anda mungkin juga menggunakan rutin impor dan ekspor.
sumber
Alternatif yang sangat umum adalah "namding padding":
Ini tidak menganggap struktur tidak akan melangkah ke 8 byte.
sumber