Untuk pertanyaan tentang U-Boot

Temukan alasan untuk semuanya dan Anda akan mengerti banyak.


Baru-baru ini, melihat kode U-Boot di bagian implementasi SPI, saya menemukan makro untuk mem-bypass daftar perangkat yang tersedia, yang setelah beberapa transisi mengatur ulang saya ke makro container_of. Teks makro itu sendiri hadir, dan dengan sedikit heran saya melihat bahwa itu agak berbeda dari versi yang sebelumnya saya lihat. Investigasi kecil dilakukan, yang menghasilkan hasil yang menarik.

Makro itu sendiri telah dikenal untuk waktu yang lama dan memecahkan masalah yang agak aneh: kita memiliki penunjuk ke bidang beberapa struktur (ptr), kita tahu jenis struktur (jenis) dan nama bidang (anggota), dan kita perlu mendapatkan penunjuk ke struktur secara keseluruhan. Saya tidak benar-benar mengerti bagaimana tugas seperti itu dapat muncul, mungkin penulis "menginginkan sesuatu yang aneh," tetapi begitu tugas itu ditetapkan, itu perlu diselesaikan. Solusinya dikenal:

#define container_of(ptr, type, member) \ ((type *)((char *)(ptr)-offsetof(type,member))) 

Semuanya transparan dan jelas, tetapi implementasi yang ditemukan agak lebih rumit:

 #define container_of(ptr, type, member) ({ \ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ (type *)( (char *)__mptr - offsetof(type,member) );}) 

Baris kedua hampir identik dengan solusi pertama, tetapi apa yang dilakukan baris pertama makro? Tidak, apa yang dilakukannya hanya dapat dimengerti: ia menciptakan pointer lokal konstan ke bidang struktur dan menetapkannya nilai parameter makro - pointer ke bidang. Tetapi mengapa ini dilakukan tidak jelas.

Pertimbangan yang jelas mengenai tujuan baris pertama adalah untuk memeriksa parameter pertama makro yang benar-benar merupakan penunjuk ke bidang struktur dalam gaya:

  int *x = (X) 

yang dalam hal ini mengambil bentuk yang agak muskil:

 typeof( ((type *)0)->member ) *__mptr = (ptr) 

karena jenis yang diperlukan untuk verifikasi harus dibangun dengan cepat.

Oke, mari kita setuju, masalahnya berguna, meskipun, saya harus menggunakan pointer yang dihasilkan di baris kedua untuk menghindari peringatan kompiler.

Tapi mengapa pengubah keteguhan - makro harus bekerja dengan baik bahkan tanpa penambahan ini (saya segera mencoba dan itu berhasil). Para penulis tidak dapat membuatnya secara kebetulan.

Tidak perlu melihat ke sana
Saya harus mengakui bahwa mereka memberi saya jawabannya, saya sendiri tidak mengira.

Ternyata pengubah ini hanya diperlukan jika kita mencoba mencari alamat struktur konstan, karena dalam hal ini kompiler akan memerlukan invalid conversion from 'const xxx*' to `xxx*' .

Sangat menarik bahwa keteguhan bidang itu sendiri di dalam struktur biasa tidak hanya tidak memerlukan paksaan dalam makro, tetapi juga menghilangkan kebutuhannya akan struktur yang konstan, yaitu, ekspresi seperti:

 struct s1 { int data; const int next; }; const struct s1 ss1 = {314,0}; container_of(&(ss1.next),struct s1,next); 

kompilasi tanpa kesalahan dan tanpa pengubah dalam teks makro, dan ekspresi:

 struct s1 { int data; int next; }; const struct s1 ss1 = {314,0}; container_of(&(ss1.next),struct s1,next); 

itu wajib.

Bagi para pembaca Habr yang tahu standar bahasa dengan baik, penemuan saya akan tampak konyol dalam gaya "Terima kasih, kapten", tetapi untuk sisanya mereka mungkin menarik.

Source: https://habr.com/ru/post/id466905/


All Articles