C'deki eşitlik yapılarını nasıl karşılaştırıyorsunuz?


Yanıtlar:


196

C bunu yapmak için herhangi bir dil olanağı sunmaz - bunu kendiniz yapmanız ve her yapı üyesini üye ile karşılaştırmanız gerekir.


19
2 yapı değişkeni calloc ile başlıyorsa veya memset ile 0 olarak ayarlandıysa, 2 yapınızı memcmp ile karşılaştırabilir ve yapı çöpüyle ilgili endişe duymazsınız ve bu zaman kazanmanıza izin verir
MOHAMED

21
@MOHAMED Kayan nokta alanlarıyla karşılaştırmak 0.0, -0.0 NaNbir problemdir memcmp(). İkili gösterimde farklılık gösteren işaretçiler aynı konuma işaret edebilir (örn. DOS: seg: offset) ve bu nedenle eşittir. Bazı sistemlerde eşit olarak karşılaştırılan birden fazla boş gösterici bulunur. intGereksiz kodlamalara sahip -0 ve kayan nokta türleri için belirsiz . (Intel long double, ondalık64 vb.) Bu sorunlar calloc()kullanılmaz veya kullanılmaz veya dolgu olmaz.
chux - Monica'yı eski durumuna döndür

2
@chux Bildiğim herhangi bir modern 32 veya 64 bit sistemde tek sorun kayan nokta.
Demi

2
Neden ==benim gibi yapılarla çalışmadığını merak ediyorsanız , lütfen bkz. Stackoverflow.com/questions/46995631/…
stefanct

4
@Demi: Bugün. C programcıları için 10. emir, “Tüm dünyanın bir VAX” olduğunu iddia eden aşağılık sapkınlıktan vazgeçeceksiniz, vazgeçeceksiniz ve feragat edeceksiniz ... ”. Bunu "Tüm dünyadaki bir PC" ile değiştirmek bir gelişme değil.
Martin Bonner

110

Kullanmak isteyebilirsiniz memcmp(&a, &b, sizeof(struct foo)), ancak her durumda çalışmayabilir. Derleyici bir yapıya hizalama arabellek alanı ekleyebilir ve ara bellek alanında bulunan bellek konumlarında bulunan değerlerin belirli bir değer olacağı garanti edilmez.

Kullanmak Ama eğer callocyoksa memsetbunları kullanmadan önce yapıların tam boy, sen yapabilirsiniz bir yapmak sığ olan karşılaştırma memcmp(Yapınızın işaretçileri içeriyorsa işaretçileri işaret ediyor adresi aynı ise, sadece maç olacak).


19
Kapat, çünkü "hemen hemen tüm" derleyiciler üzerinde çalışıyor, ama tam olarak değil. C90'da 6.2.1.6.4'e göz atın: "Aynı nesne gösterimi olan iki değer (NaN'ler dışında) eşit karşılaştırır, ancak eşitliği karşılaştıran değerler farklı nesne gösterimlerine sahip olabilir."
Steve Jessop

22
Bir "BOOL" alanı düşünün. Eşitlik açısından, sıfır olmayan herhangi bir BOOL, sıfır olmayan her BOOL değerine eşittir. Bu nedenle, 1 ve 2'nin ikisi de TRUE ve dolayısıyla eşit olsa da, memcmp başarısız olur.
ajs410

4
@JSalazar Sizin için daha kolay, ancak derleyici ve CPU için çok daha zor ve dolayısıyla daha yavaş. Sizce neden derleyici dolgu ekliyor? Kesinlikle hiçbir şey için hafızayı boşa harcamamak;)
Mecki

4
@Demetri: örneğin pozitif ve negatif sıfır şamandıra değerleri, herhangi bir IEEE şamandıra uygulamasında eşittir, ancak aynı nesne temsiline sahip değildirler. Bu yüzden aslında "hemen hemen tüm derleyiciler" üzerinde çalıştığını söylememeliydim, negatif sıfır depolamanızı sağlayan herhangi bir uygulamada başarısız olacaktır. Muhtemelen yorum yaptığım sırada komik tamsayı temsillerini düşünüyordum.
Steve Jessop

4
@Demetri: ancak birçoğu şamandıra içeriyor ve soru soran "şamandıra içermeyen yapıları nasıl karşılaştırıyorsunuz" değil, "yapıları nasıl karşılaştırıyorsunuz" diye soruyor. Bu cevap memcmphafızanın ilk önce temizlenmesi şartıyla sığ bir karşılaştırma yapabileceğinizi söylüyor . Hangi çalışmaya yakın ama doğru değil. Ofc sorusu aynı zamanda "eşitlik" i tanımlamaz, bu yüzden eğer bunu "nesne gösteriminin bayt-bilge eşitliği" memcmpolarak adlandırırsanız, tam olarak bunu yapar (hafızanın silinip temizlenmediği).
Steve Jessop

22

Çok yaparsanız, iki yapıyı karşılaştıran bir fonksiyon yazmanızı öneririm. Bu şekilde, yapıyı değiştirirseniz, karşılaştırmayı tek bir yerde değiştirmeniz yeterlidir.

Nasıl yapılacağı konusunda .... Her elemanı ayrı ayrı karşılaştırmanız gerekir


1
Sadece bir kez kullanacak olsam bile ayrı bir fonksiyon yazardım.
Sam

18

Memcmp'yi, yapılardaki alan arasındaki olası rastgele doldurma karakterleri nedeniyle eşitlik yapılarını karşılaştırmak için kullanamazsınız.

  // bad
  memcmp(&struct1, &struct2, sizeof(struct1));

Yukarıdaki gibi bir yapı için başarısız olacaktır:

typedef struct Foo {
  char a;
  /* padding */
  double d;
  /* padding */
  char e;
  /* padding */
  int f;
} Foo ;

Güvende olmak için üye bazında karşılaştırmayı kullanmalısınız.


25
Çifte sonra dolgu olması muhtemel değildir; karakter çiftin hemen ardından mükemmel bir şekilde hizalanacaktır.
Jonathan Leffler

7

@Greg, genel durumda açık karşılaştırma işlevleri yazmak zorunda olduğu doğrudur.

Aşağıdaki durumlarda kullanmak mümkündür memcmp:

  • yapılar muhtemelen kayan nokta alanı içermez NaN.
  • yapılar dolgu içermez ( -Wpaddedbunu kontrol etmek için clang ile kullanın ) VEYA yapılar başlangıçta açıkça memsetbaşlatılır.
  • BOOLfarklı ancak eşdeğer değerlere sahip üye türleri (Windows gibi ) yoktur .

Gömülü sistemler için programlama yapmıyorsanız (veya bunlarda kullanılabilecek bir kitaplık yazmıyorsanız), C standardındaki bazı köşe durumları için endişe etmem. Yakın veya uzak işaretçi ayrımı 32 veya 64 bit aygıtlarda mevcut değildir. Bildiğim gömülü olmayan sistemde birden fazla NULLişaretçi yok.

Başka bir seçenek de eşitlik işlevlerini otomatik olarak oluşturmaktır. Yapı tanımlarınızı basit bir şekilde düzenlerseniz, basit yapı tanımlarını işlemek için basit metin işlemeyi kullanmak mümkündür. Genel durum için libclang kullanabilirsiniz - Clang ile aynı ön ucu kullandığından, tüm köşe vakalarını doğru şekilde işler (engelleme hataları).

Böyle bir kod üretme kütüphanesi görmedim. Ancak, nispeten basit görünüyor.

Bununla birlikte, bu tür oluşturulan eşitlik işlevlerinin uygulama düzeyinde genellikle yanlış bir şey yapması da söz konusudur. Örneğin, UNICODE_STRINGWindows'daki iki yapı sığ mı yoksa derinden mi karşılaştırılmalıdır?



4

Tüm üyeleri (bir kerede) başlatmadığınız sürece, doldurma konusunda endişelenmeden statik olmayan yapılarda memcmp () kullanabileceğinizi unutmayın. Bu C90 ile tanımlanır:

http://www.pixelbeat.org/programming/gcc/auto_init.html


1
{0, }Ayrıca herhangi bir dolgu baytını sıfırlayacak şekilde belirtilmiş mi?
Alnitak

GCC, yukarıdaki bağlantıda gösterildiği gibi kısmen başlatılan yapılar için dolgu baytlarını en az sıfırlar ve C11'in bu davranışı belirttiği stackoverflow.com/questions/13056364/… ayrıntıları.
pixelbeat

1
Genel olarak çok kullanışlı değil, çünkü herhangi bir üyeye tayin edildiğinde tüm dolgu belirsizleşiyor
MM

2

Bu, sorduğunuz sorunun olup olmadığına bağlıdır:

  1. Bu iki yapı aynı nesne midir?
  2. Aynı değere sahipler mi?

Aynı nesne olup olmadıklarını öğrenmek için, işaretçileri eşitlik için iki yapı ile karşılaştırın. Genel olarak aynı değere sahip olup olmadıklarını öğrenmek istiyorsanız, derin bir karşılaştırma yapmanız gerekir. Bu, tüm üyelerin karşılaştırılmasını içerir. Üyeler başka yapılara işaret ederse, bu yapılara da başvurmanız gerekir.

Yapıların işaretçi içermediği özel durumda, verilerin ne anlama geldiğini bilmek zorunda kalmadan her birinde bulunan verilerin bitsel olarak karşılaştırılmasını yapmak için bir memcmp yapabilirsiniz.

Her üye için 'eşittir' ne anlama geldiğini bildiğinizden emin olun - ints için açıktır, ancak kayan nokta değerleri veya kullanıcı tanımlı türler söz konusu olduğunda daha incedir.


2

memcmpyapıyı memcmpkarşılaştırmaz, ikiliyi karşılaştırır ve yapıda her zaman çöp vardır, bu nedenle her zaman karşılaştırmada Yanlış çıkar.

Öğeyi öğeye göre güvenli olarak karşılaştırın ve başarısız olmaz.


1
2 yapı değişkeni calloc ile başlıyorsa veya memset ile 0 olarak ayarlandıysa, 2 yapınızı memcmp ile karşılaştırabilir ve yapı çöpüyle ilgili endişe duymazsınız ve bu zaman kazanmanıza izin verir
MOHAMED

1

Yapılar sadece ilkel içeriyorsa veya katı eşitlikle ilgileniyorsanız, böyle bir şey yapabilirsiniz:

int my_struct_cmp (const struct my_struct * lhs, const yapısal my_struct * rhs)
{
    dönüş memcmp (lhs, rsh, sizeof (struct my_struct));
}

Bununla birlikte, yapılarınız diğer yapılara veya sendikalara işaretçiler içeriyorsa, ilkelleri doğru bir şekilde karşılaştıran bir işlev yazmanız ve diğer yapılarla uygun şekilde karşılaştırma çağrıları yapmanız gerekecektir.

Ancak, ADT başlatmanızın bir parçası olarak yapıların bellek aralığını sıfırlamak için memset (& a, sizeof (struct my_struct), 1) kullanmanız gerektiğini unutmayın.


-1

2 yapı değişkeni calloc ile başlıyorsa veya memset ile 0 olarak ayarlandıysa, 2 yapınızı memcmp ile karşılaştırabilir ve yapı çöpüyle ilgili endişe duymazsınız ve bu da zaman kazanmanızı sağlar.


-2

Bu uyumlu örnek, yapı üyelerinin olabildiğince sıkı paketlenmesini sağlamak için Microsoft Visual Studio'dan #pragma pack derleyici uzantısını kullanır:

#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))) {
    /* ... */
  }
}

1
Bu gerçekten doğru. Ancak çoğu durumda yapılarınızın paketlenmesini istemezsiniz! Oldukça fazla talimat ve işaretçi, giriş verisinin kelime hizalı olmasını gerektirir. Değilse, derleyicinin gerçek talimat yürütülmeden önce verileri kopyalamak ve yeniden hizalamak için ek talimatlar eklemesi gerekir. Derleyici verileri yeniden hizalamazsa, CPU bir istisna atar.
Ruud Althuizen
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.