Dalam kode kernel Linux saya menemukan hal berikut yang tidak dapat saya mengerti.
struct bts_action {
u16 type;
u16 size;
u8 data[0];
} __attribute__ ((packed));
Kode di sini: http://lxr.free-electrons.com/source/include/linux/ti_wilink_st.h
Apa kebutuhan dan tujuan dari sebuah array data dengan elemen nol?
c
structure
flexible-array-member
Jeegar Patel
sumber
sumber
Jawaban:
Ini adalah cara untuk memiliki ukuran data variabel, tanpa harus memanggil
malloc
(kmalloc
dalam kasus ini) dua kali. Anda akan menggunakannya seperti ini:Ini dulunya tidak standar dan dianggap hack (seperti kata Aniket), tetapi distandarisasi di C99 . Format standarnya sekarang adalah:
Perhatikan bahwa Anda tidak menyebutkan ukuran apa pun untuk
data
bidang tersebut. Perhatikan juga bahwa variabel khusus ini hanya dapat muncul di akhir struct.Dalam C99, hal ini dijelaskan dalam 6.7.2.1.16 (penekanan saya):
Atau dengan kata lain, jika Anda memiliki:
Anda dapat mengakses
var->data
dengan indeks di[0, extra)
. Perhatikan bahwasizeof(struct something)
hanya akan memberikan akuntansi ukuran untuk variabel lain, yaitu memberikandata
ukuran 0.Mungkin menarik juga untuk mencatat bagaimana standar sebenarnya memberikan contoh
malloc
konstruksi seperti itu (6.7.2.1.17):Catatan menarik lainnya menurut standar di lokasi yang sama adalah (penekanan saya):
sumber
[0, extra)
?Ini sebenarnya adalah retasan, untuk GCC ( C90 ) sebenarnya.
Ini juga disebut hack struct .
Jadi lain kali, saya akan mengatakan:
Ini akan sama dengan mengatakan:
Dan saya dapat membuat sejumlah objek struct semacam itu.
sumber
Idenya adalah untuk memungkinkan array berukuran variabel di akhir struct. Agaknya,
bts_action
adalah beberapa paket data dengan header berukuran tetap ( bidangtype
dansize
), dandata
anggota berukuran variabel . Dengan mendeklarasikannya sebagai larik dengan panjang 0, ia dapat diindeks sama seperti larik lainnya. Anda kemudian akan mengalokasikanbts_action
struct, katakanlahdata
ukuran 1024-byte , seperti ini:Lihat juga: http://c2.com/cgi/wiki?StructHack
sumber
malloc
Anda membuat Anda berulang kali dan jika ada jenisaction
perubahan, Anda harus memperbaikinya beberapa kali. Bandingkan dua hal berikut ini untuk Anda sendiri dan Anda akan tahu:struct some_thing *variable = (struct some_thing *)malloc(10 * sizeof(struct some_thing));
vs.struct some_thing *variable = malloc(10 * sizeof(*variable));
Yang kedua lebih pendek, lebih bersih dan jelas lebih mudah diubah.Kode tersebut tidak valid C ( lihat ini ). Kernel Linux, karena alasan yang jelas, sama sekali tidak peduli dengan portabilitas, jadi ia menggunakan banyak kode non-standar.
Apa yang mereka lakukan adalah ekstensi non-standar GCC dengan ukuran larik 0. Program yang memenuhi standar akan menulis
u8 data[];
dan artinya akan sama. Penulis kernel Linux tampaknya suka membuat hal-hal yang tidak perlu menjadi rumit dan tidak standar, jika opsi untuk melakukannya muncul dengan sendirinya.Dalam standar C yang lebih lama, mengakhiri struct dengan array kosong dikenal sebagai "the struct hack". Orang lain telah menjelaskan tujuannya dalam jawaban lain. Peretasan struct, dalam standar C90, adalah perilaku yang tidak ditentukan dan dapat menyebabkan crash, terutama karena kompiler C bebas untuk menambahkan sejumlah byte padding di akhir struct. Byte padding tersebut dapat bertabrakan dengan data yang Anda coba "retas" di akhir struct.
GCC sejak awal membuat ekstensi non-standar untuk mengubahnya dari perilaku tidak terdefinisi menjadi perilaku terdefinisi dengan baik. Standar C99 kemudian mengadaptasi konsep ini dan program C modern apa pun dapat menggunakan fitur ini tanpa risiko. Ini dikenal sebagai anggota larik fleksibel di C99 / C11.
sumber
Penggunaan lain dari array panjang nol adalah sebagai label bernama di dalam struct untuk membantu waktu kompilasi pemeriksaan offset struct.
Misalkan Anda memiliki beberapa definisi struct besar (mencakup beberapa baris cache) yang ingin Anda pastikan mereka sejajar dengan batas baris cache baik di awal dan di tengah di mana ia melintasi batas.
Dalam kode, Anda dapat mendeklarasikannya menggunakan ekstensi GCC seperti:
Namun Anda tetap ingin memastikan ini diterapkan dalam runtime.
Ini akan berfungsi untuk satu struct, tetapi akan sulit untuk mencakup banyak struct, masing-masing memiliki nama anggota yang berbeda untuk disejajarkan. Anda kemungkinan besar akan mendapatkan kode seperti di bawah ini di mana Anda harus menemukan nama anggota pertama dari setiap struct:
Alih-alih menggunakan cara ini, Anda dapat mendeklarasikan array dengan panjang nol di struct yang bertindak sebagai label bernama dengan nama yang konsisten tetapi tidak menggunakan spasi.
Maka kode pernyataan waktu proses akan jauh lebih mudah dikelola:
sumber