Bagaimana Anda membandingkan dua contoh struct untuk kesetaraan dalam standar C?
Bagaimana Anda membandingkan dua contoh struct untuk kesetaraan dalam standar C?
Jawaban:
C tidak menyediakan fasilitas bahasa untuk melakukan ini - Anda harus melakukannya sendiri dan membandingkan setiap anggota struktur dengan anggota.
0.0, -0.0 NaN
masalah memcmp()
. Pointer yang berbeda dalam representasi biner dapat menunjuk ke lokasi yang sama (misalnya DOS: seg: offset) dan sama. Beberapa sistem memiliki beberapa pointer nol yang membandingkan secara merata. Sama untuk yang tidak jelas int
dengan -0 dan tipe floating point dengan pengkodean redundan. (Intel long double, decimal64, dll.) Masalah-masalah ini tidak membuat perbedaan calloc()
digunakan atau tidak atau padding.
==
tidak bekerja dengan struktur (seperti saya), silakan lihat stackoverflow.com/questions/46995631/…
Anda mungkin tergoda untuk menggunakannya memcmp(&a, &b, sizeof(struct foo))
, tetapi mungkin tidak berfungsi dalam semua situasi. Kompiler dapat menambahkan ruang penyejajaran penyelarasan ke struktur, dan nilai-nilai yang ditemukan di lokasi memori yang terletak di ruang buffer tidak dijamin menjadi nilai tertentu.
Tetapi, jika Anda menggunakan calloc
atau memset
ukuran penuh dari struktur sebelum menggunakannya, Anda dapat melakukan perbandingan yang dangkalmemcmp
(jika struktur Anda berisi pointer, itu hanya akan cocok jika alamat yang ditunjuk pointer adalah sama).
memcmp
asalkan memori telah dihapus terlebih dahulu. Yang dekat dengan bekerja tetapi tidak benar. Ofc pertanyaannya juga tidak mendefinisikan "kesetaraan", jadi jika Anda menganggapnya "kesetaraan byte-bijaksana dari representasi objek" kemudian memcmp
melakukan hal itu (apakah memori dihapus atau tidak).
Jika Anda sering melakukannya saya sarankan menulis fungsi yang membandingkan dua struktur. Dengan begitu, jika Anda pernah mengubah struktur, Anda hanya perlu mengubah perbandingan di satu tempat.
Adapun cara melakukannya .... Anda harus membandingkan setiap elemen secara individual
Anda tidak dapat menggunakan memcmp untuk membandingkan struct untuk kesetaraan karena potensi karakter padding acak antara bidang dalam struct.
// bad
memcmp(&struct1, &struct2, sizeof(struct1));
Di atas akan gagal untuk struct seperti ini:
typedef struct Foo {
char a;
/* padding */
double d;
/* padding */
char e;
/* padding */
int f;
} Foo ;
Anda harus menggunakan perbandingan anggota-bijaksana agar aman.
@Reg benar bahwa seseorang harus menulis fungsi perbandingan eksplisit dalam kasus umum.
Dimungkinkan untuk digunakan memcmp
jika:
NaN
.-Wpadded
dengan dentang untuk memeriksa ini) ATAU struct secara eksplisit diinisialisasi dengan memset
saat inisialisasi.BOOL
) yang memiliki nilai berbeda tetapi setara.Kecuali Anda pemrograman untuk embedded system (atau menulis perpustakaan yang mungkin digunakan pada mereka), saya tidak akan khawatir tentang beberapa kasus sudut dalam standar C. Perbedaan pointer dekat vs jauh tidak ada pada perangkat 32- atau 64-bit. Tidak ada sistem non-embedded yang saya tahu memiliki banyak NULL
pointer.
Pilihan lain adalah untuk menghasilkan fungsi kesetaraan secara otomatis. Jika Anda meletakkan definisi struct Anda dengan cara yang sederhana, dimungkinkan untuk menggunakan pemrosesan teks sederhana untuk menangani definisi struct yang sederhana. Anda dapat menggunakan libclang untuk case umum - karena menggunakan frontend yang sama dengan Clang, ia menangani semua case sudut dengan benar (bug pembatas).
Saya belum melihat perpustakaan pembuatan kode seperti itu. Namun, tampaknya relatif sederhana.
Namun, ini juga merupakan kasus dimana fungsi kesetaraan yang dihasilkan seperti itu sering melakukan hal yang salah pada level aplikasi. Misalnya, haruskah dua UNICODE_STRING
struct di Windows dibandingkan secara dangkal atau dalam?
memset
, dll. Tidak menjamin nilai bit padding setelah menulis lebih lanjut ke elemen struct, lihat: stackoverflow.com/q/52684192/689161
Catatan Anda dapat menggunakan memcmp () pada struktur yang tidak statis tanpa khawatir tentang bantalan, selama Anda tidak menginisialisasi semua anggota (sekaligus). Ini didefinisikan oleh C90:
{0, }
juga akan nol byte padding?
Itu tergantung pada apakah pertanyaan yang Anda ajukan adalah:
Untuk mengetahui apakah mereka adalah objek yang sama, bandingkan pointer ke dua struct untuk kesetaraan. Jika Anda ingin mencari tahu secara umum jika mereka memiliki nilai yang sama Anda harus melakukan perbandingan yang mendalam. Ini melibatkan membandingkan semua anggota. Jika anggotanya adalah penunjuk ke struct lain Anda juga perlu recurse ke struct tersebut juga.
Dalam kasus khusus di mana struct tidak mengandung pointer, Anda dapat melakukan memcmp untuk melakukan perbandingan bitwise dari data yang terkandung dalam masing-masing tanpa harus tahu apa artinya data.
Pastikan Anda tahu apa arti 'sama dengan' untuk setiap anggota - jelas untuk int tetapi lebih halus ketika datang ke nilai floating-point atau tipe yang ditentukan pengguna.
memcmp
tidak membandingkan struktur, memcmp
membandingkan biner, dan selalu ada sampah di struct, oleh karena itu selalu keluar Palsu sebagai perbandingan.
Bandingkan elemen demi elemen yang aman dan tidak gagal.
Jika struct hanya berisi primitif atau jika Anda tertarik pada kesetaraan yang ketat maka Anda dapat melakukan sesuatu seperti ini:
int my_struct_cmp (const struct my_struct * lhs, const struct my_struct * rhs) { kembalikan memcmp (lhs, rsh, sizeof (struct my_struct)); }
Namun, jika struct Anda berisi pointer ke struct atau serikat lain maka Anda harus menulis fungsi yang membandingkan primitif dengan benar dan membuat panggilan perbandingan terhadap struktur lain yang sesuai.
Perlu diketahui, bahwa Anda harus menggunakan memset (& a, sizeof (struct my_struct), 1) untuk nol rentang memori struktur sebagai bagian dari inisialisasi ADT Anda.
Contoh patuh ini menggunakan ekstensi kompilator paket #pragma dari Microsoft Visual Studio untuk memastikan anggota struktur dikemas sekencang mungkin:
#include <string.h>
#pragma pack(push, 1)
struct s {
char c;
int i;
char buffer[13];
};
#pragma pack(pop)
void compare(const struct s *left, const struct s *right) {
if (0 == memcmp(left, right, sizeof(struct s))) {
/* ... */
}
}