Saya bertanya-tanya apakah seseorang dapat menjelaskan kepada saya apa yang dilakukan oleh #pragma pack
pernyataan preprosesor, dan yang lebih penting, mengapa seseorang ingin menggunakannya.
Saya memeriksa halaman MSDN , yang menawarkan beberapa wawasan, tetapi saya berharap untuk mendengar lebih banyak dari orang-orang yang berpengalaman. Saya sudah melihatnya dalam kode sebelumnya, meskipun saya tidak bisa menemukan tempat lagi.
c
c-preprocessor
pragma-pack
Cenoc
sumber
sumber
#pragma
arahan mereka didefinisikan implementasi.A mod s = 0
di mana A adalah alamat dan s adalah ukuran datatype; ini memeriksa apakah suatu data tidak selaras.Jawaban:
#pragma pack
menginstruksikan kompiler untuk mengemas anggota struktur dengan penyelarasan tertentu. Sebagian besar kompiler, ketika Anda mendeklarasikan struct, akan menyisipkan bantalan di antara anggota untuk memastikan bahwa mereka disejajarkan dengan alamat yang sesuai dalam memori (biasanya kelipatan dari ukuran tipe). Ini menghindari penalti kinerja (atau kesalahan langsung) pada beberapa arsitektur yang terkait dengan mengakses variabel yang tidak selaras dengan benar. Misalnya, diberikan bilangan bulat 4-byte dan struct berikut:Kompiler dapat memilih untuk meletakkan struct di memori seperti ini:
dan
sizeof(Test)
akan menjadi 4 × 3 = 12, meskipun hanya berisi 6 byte data. Kasus penggunaan yang paling umum untuk#pragma
(sepengetahuan saya) adalah ketika bekerja dengan perangkat perangkat keras di mana Anda perlu memastikan bahwa kompiler tidak memasukkan bantalan ke dalam data dan setiap anggota mengikuti yang sebelumnya. Dengan#pragma pack(1)
, struct di atas akan ditata seperti ini:Dan
sizeof(Test)
akan menjadi 1 × 6 = 6.Dengan
#pragma pack(2)
, struct di atas akan ditata seperti ini:Dan
sizeof(Test)
akan menjadi 2 × 4 = 8.Urutan variabel dalam struct juga penting. Dengan variabel yang dipesan seperti berikut:
dan dengan
#pragma pack(2)
, struct akan ditata seperti ini:dan
sizeOf(Test)
akan menjadi 3 × 2 = 6.sumber
#pragma
digunakan untuk mengirim pesan non-portabel (seperti dalam kompiler ini saja) ke kompiler. Hal-hal seperti menonaktifkan peringatan dan pengemasan struct tertentu adalah alasan umum. Menonaktifkan peringatan spesifik sangat berguna jika Anda mengkompilasi dengan peringatan saat bendera kesalahan diaktifkan.#pragma pack
khusus digunakan untuk menunjukkan bahwa struct yang sedang dikemas tidak harus memiliki anggota yang disejajarkan. Ini berguna ketika Anda memiliki antarmuka yang dipetakan memori ke perangkat keras dan harus dapat mengontrol persis di mana anggota struct yang berbeda menunjuk. Ini terutama bukan optimasi kecepatan yang baik, karena sebagian besar mesin jauh lebih cepat dalam menangani data yang disejajarkan.sumber
Ini memberitahu kompiler batas untuk menyelaraskan objek dalam suatu struktur. Misalnya, jika saya memiliki sesuatu seperti:
Dengan mesin 32-bit yang khas, Anda biasanya "ingin" memiliki 3 byte padding di antara
a
danb
sehinggab
akan mendarat pada batas 4-byte untuk memaksimalkan kecepatan aksesnya (dan itulah yang biasanya akan terjadi secara default).Namun, jika Anda harus mencocokkan struktur yang ditentukan secara eksternal, Anda ingin memastikan kompiler menjabarkan struktur Anda persis sesuai dengan definisi eksternal itu. Dalam hal ini, Anda dapat memberikan kompiler
#pragma pack(1)
untuk mengatakannya agar tidak menyisipkan bantalan di antara anggota - jika definisi struktur mencakup bantalan di antara anggota, Anda memasukkannya secara eksplisit (misalnya, biasanya dengan anggota bernamaunusedN
atauignoreN
, atau sesuatu di atasnya memesan).sumber
b
pada batas 4-byte berarti prosesor dapat memuatnya dengan mengeluarkan beban 4-byte tunggal. Meskipun agak tergantung pada prosesor, jika berada pada batas ganjil, ada peluang bagus untuk memuatnya akan mengharuskan prosesor mengeluarkan dua instruksi pemuatan yang berbeda, kemudian gunakan shifter untuk menyatukan potongan-potongan tersebut. Penalti umum ada di urutan 3x lebih lambat dari item itu.Elemen data (misalnya anggota kelas dan struct) biasanya diselaraskan pada batas WORD atau DWORD untuk prosesor generasi saat ini untuk meningkatkan waktu akses. Mengambil DWORD pada alamat yang tidak dapat dibagi oleh 4 membutuhkan setidaknya satu siklus CPU tambahan pada prosesor 32 bit. Jadi, jika Anda memiliki misalnya tiga anggota char
char a, b, c;
, mereka sebenarnya cenderung mengambil 6 atau 12 byte penyimpanan.#pragma
memungkinkan Anda untuk mengesampingkan ini untuk mencapai penggunaan ruang yang lebih efisien, dengan mengorbankan kecepatan akses, atau untuk konsistensi data yang tersimpan di antara berbagai target penyusun. Saya bersenang-senang dengan transisi dari 16 bit ke 32 bit kode; Saya berharap porting ke kode 64 bit akan menyebabkan jenis sakit kepala yang sama untuk beberapa kode.sumber
char a,b,c;
biasanya akan mengambil 3 atau 4 byte penyimpanan (setidaknya x86) - itu karena persyaratan penyelarasannya adalah 1 byte. Jika tidak, lalu bagaimana Anda akan menghadapinyachar str[] = "foo";
? Akses ke achar
selalu berupa fetch-shift-mask yang sederhana, sementara akses ke aint
bisa berupa fetch-fetch-merge atau hanya mengambil, tergantung pada apakah itu sejajar atau tidak.int
memiliki (pada x86) perataan 32-bit (4 byte) karena jika tidak, Anda akan mendapatkan (katakanlah) setengahint
dalam satuDWORD
dan setengah lainnya, dan itu akan membutuhkan dua pencarian.Compiler dapat menyelaraskan anggota dalam struktur untuk mencapai kinerja maksimum pada platform tertentu.
#pragma pack
arahan memungkinkan Anda untuk mengontrol perataan itu. Biasanya Anda harus membiarkannya secara default untuk kinerja optimal. Jika Anda harus meneruskan struktur ke mesin jarak jauh yang biasanya Anda gunakan#pragma pack 1
untuk mengecualikan penyelarasan yang tidak diinginkan.sumber
Kompiler dapat menempatkan anggota struktur pada batas byte tertentu untuk alasan kinerja pada arsitektur tertentu. Ini dapat menyebabkan bantalan yang tidak digunakan di antara anggota. Packing struktur memaksa anggota untuk berdekatan.
Ini mungkin penting misalnya jika Anda memerlukan struktur agar sesuai dengan file atau format komunikasi tertentu di mana data yang Anda perlukan data berada pada posisi tertentu dalam suatu urutan. Namun penggunaan seperti itu tidak berurusan dengan masalah endian-ness, jadi meskipun digunakan, itu mungkin tidak portabel.
Mungkin juga untuk overlay struktur register internal dari beberapa perangkat I / O seperti pengontrol UART atau USB misalnya, agar akses register melalui struktur daripada alamat langsung.
sumber
Anda mungkin hanya ingin menggunakan ini jika Anda mengkodekan ke beberapa perangkat keras (misalnya perangkat yang dipetakan memori) yang memiliki persyaratan ketat untuk pemesanan register dan penyelarasan.
Namun, ini terlihat seperti alat yang cukup tumpul untuk mencapai tujuan itu. Pendekatan yang lebih baik adalah dengan kode driver mini di assembler dan memberinya antarmuka panggilan C daripada meraba-raba dengan pragma ini.
sumber
Saya sudah menggunakannya dalam kode sebelumnya, meskipun hanya untuk antarmuka dengan kode lama. Ini adalah aplikasi Mac OS X Cocoa yang perlu memuat file preferensi dari versi Carbon sebelumnya (yang sendiri kompatibel dengan versi M68k System 6.5 yang asli ... Anda dapat idenya). File preferensi dalam versi asli adalah dump biner dari struktur konfigurasi, yang menggunakan
#pragma pack(1)
untuk menghindari mengambil ruang tambahan dan menghemat sampah (yaitu byte padding yang seharusnya berada dalam struktur).Penulis asli kode juga digunakan
#pragma pack(1)
untuk menyimpan struktur yang digunakan sebagai pesan dalam komunikasi antar-proses. Saya pikir alasannya di sini adalah untuk menghindari kemungkinan ukuran padding yang tidak diketahui atau diubah, karena kode kadang-kadang melihat bagian tertentu dari struct pesan dengan menghitung sejumlah byte dari awal (ewww).sumber
Saya telah melihat orang menggunakannya untuk memastikan bahwa struktur mengambil seluruh garis cache untuk mencegah kesalahan berbagi dalam konteks multithreaded. Jika Anda akan memiliki sejumlah besar objek yang akan dikemas secara default, itu dapat menghemat memori dan meningkatkan kinerja cache untuk mengemasnya lebih ketat, meskipun akses memori yang tidak selaras biasanya akan memperlambat segalanya sehingga mungkin ada sisi negatifnya.
sumber
Perhatikan bahwa ada cara lain untuk mencapai konsistensi data yang ditawarkan oleh paket #pragma (misalnya beberapa orang menggunakan paket #pragma (1) untuk struktur yang harus dikirim melalui jaringan). Sebagai contoh, lihat kode berikut dan output selanjutnya:
Outputnya adalah sebagai berikut: sizeof (struct a): 15, sizeof (struct b): 24 sizeof (twoa): 30, sizeof (twob): 48
Perhatikan bagaimana ukuran struct a persis apa jumlah byte, tetapi struct b telah menambahkan padding (lihat ini untuk detail tentang padding). Dengan melakukan ini sebagai lawan dari paket #pragma, Anda dapat memiliki kendali untuk mengubah "format kawat" menjadi tipe yang sesuai. Misalnya, "char two [2]" menjadi "int pendek" dan sebagainya.
sumber
sizeof
mengembalikansize_t
yang harus dicetak menggunakan%zu
. Menggunakan format specifier yang salah memunculkan perilaku yang tidak terdefinisiMengapa seseorang ingin menggunakannya?
Untuk mengurangi memori struktur
Mengapa orang tidak menggunakannya?
sumber