Come si confrontano le strutture per l'uguaglianza in C?


Risposte:


196

C non offre strutture linguistiche per farlo - devi farlo tu stesso e confrontare ogni membro della struttura per membro.


19
se le variabili delle 2 strutture sono inizializzate con calloc o sono impostate con 0 dal memset in modo da poter confrontare le tue 2 strutture con memcmp e non c'è preoccupazione per la spazzatura della struttura e questo ti permetterà di guadagnare tempo
MOHAMED

21
@MOHAMED Il confronto dei campi in virgola mobile con 0.0, -0.0 NaNè un problema memcmp(). I puntatori che differiscono nella rappresentazione binaria possono puntare alla stessa posizione (es. DOS: seg: offset) e quindi sono uguali. Alcuni sistemi hanno più puntatori null che si confrontano equamente. Lo stesso per oscuri intcon -0 e tipi in virgola mobile con codifiche ridondanti. (Intel long double, decimal64, ecc.) Questi problemi non fanno differenza calloc()o vengono utilizzati o riempiti.
chux - Ripristina Monica il

2
@chux Su qualsiasi moderno sistema a 32 o 64 bit che conosco, l'unico problema è con il virgola mobile.
Demi,

2
Nel caso in cui ti chiedi perché ==non funziona con le strutture (come me), si prega di consultare stackoverflow.com/questions/46995631/...
stefanct

4
@Demi: oggi. Il decimo comandamento per i programmatori C è "Devi prefigurare, rinunciare e abiurare la vile eresia che afferma che" Tutto il mondo è un VAX "...". Sostituire questo con "Tutto il mondo è un PC" non è un miglioramento.
Martin Bonner supporta Monica il

110

Potresti essere tentato di usarlo memcmp(&a, &b, sizeof(struct foo)), ma potrebbe non funzionare in tutte le situazioni. Il compilatore può aggiungere spazio buffer di allineamento a una struttura e i valori trovati in posizioni di memoria che si trovano nello spazio buffer non sono garantiti come alcun valore particolare.

Ma, se usi calloco memsetl'intera dimensione delle strutture prima di usarle, puoi fare un confronto superficiale con memcmp(se la tua struttura contiene puntatori, corrisponderà solo se l'indirizzo a cui puntano i puntatori è lo stesso).


19
Chiudi, perché funziona su "quasi tutti" compilatori, ma non del tutto. Guarda 6.2.1.6.4 in C90: "Due valori (diversi dai NaN) con la stessa rappresentazione di oggetti sono uguali, ma i valori che confrontano uguali possono avere rappresentazioni di oggetti differenti."
Steve Jessop,

22
Considera un campo "BOOL". In termini di uguaglianza, qualsiasi BOOL diverso da zero è uguale a ogni valore BOOL diverso da zero. Quindi, mentre 1 e 2 possono essere entrambi TRUE e quindi uguali, memcmp fallirà.
ajs410

4
@JSalazar Forse più facile per te, ma molto più difficile per il compilatore e la CPU e quindi anche molto più lento. Perché pensi che il compilatore aggiunga il riempimento in primo luogo? Certamente non sprecare memoria per niente;)
Mecki

4
@Demetri: ad esempio i valori float positivi e negativi zero si equivalgono su qualsiasi implementazione float IEEE, ma non hanno la stessa rappresentazione di oggetti. Quindi in realtà non avrei dovuto dire che funziona su "quasi tutti i compilatori", fallirà su qualsiasi implementazione che ti permetta di memorizzare uno zero negativo. Probabilmente stavo pensando a rappresentazioni di interi divertenti al momento in cui ho fatto il commento.
Steve Jessop,

4
@Demetri: ma molti contengono float e l'interrogatore chiede "come si confrontano le strutture", non "come si confrontano le strutture che non contengono float". Questa risposta dice che puoi fare un confronto superficiale con memcmppurché la memoria sia stata prima cancellata. Che è vicino al lavoro ma non corretto. Inoltre, la domanda non definisce "uguaglianza", quindi se la prendi nel significato di "uguaglianza byte-byte della rappresentazione dell'oggetto", lo memcmpfa esattamente (se la memoria viene cancellata o meno).
Steve Jessop,

22

Se lo fai molto suggerirei di scrivere una funzione che confronta le due strutture. In questo modo, se dovessi mai cambiare la struttura, dovrai solo cambiare il confronto in un unico posto.

Per quanto riguarda come farlo .... Devi confrontare ogni elemento singolarmente


1
Scriverei una funzione separata anche se la usassi solo una volta.
Sam,

18

Non è possibile utilizzare memcmp per confrontare le strutture per l'uguaglianza a causa di potenziali caratteri di riempimento casuale tra il campo nelle strutture.

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

Quanto sopra fallirebbe per una struttura come questa:

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

Devi usare il confronto tra i membri per essere sicuro.


25
Difficilmente sarà imbottito dopo il doppio; il carattere sarà perfettamente allineato perfettamente immediatamente dopo il doppio.
Jonathan Leffler,

7

@Greg ha ragione nel dire che è necessario scrivere funzioni di confronto esplicite nel caso generale.

È possibile utilizzare memcmpse:

  • le strutture non contengono campi in virgola mobile che potrebbero essere NaN.
  • le strutture non contengono imbottitura (usare -Wpaddedcon clang per verificare ciò) OPPURE le strutture sono inizializzate esplicitamente con l' memsetinizializzazione.
  • non ci sono tipi di membri (come Windows BOOL) che hanno valori distinti ma equivalenti.

A meno che tu non stia programmando per sistemi embedded (o scrivendo una libreria che potrebbe essere usata su di essi), non mi preoccuperei di alcuni casi angolari nello standard C. La distinzione tra puntatore vicino e lontano non esiste su nessun dispositivo a 32 o 64 bit. Nessun sistema non incorporato che conosco ha più NULLpuntatori.

Un'altra opzione è di generare automaticamente le funzioni di uguaglianza. Se disponi le definizioni di struct in modo semplice, è possibile utilizzare una semplice elaborazione di testo per gestire definizioni di struct semplici. Puoi usare libclang per il caso generale, poiché usa lo stesso frontend di Clang, gestisce correttamente tutti i casi angolari (escludendo i bug).

Non ho visto una tale libreria di generazione di codice. Tuttavia, sembra relativamente semplice.

Tuttavia, è anche vero che tali funzioni di uguaglianza generate spesso farebbero la cosa sbagliata a livello di applicazione. Ad esempio, due UNICODE_STRINGstrutture in Windows devono essere confrontate in modo superficiale o profondo?


2
Esplicitamente inizializzare le struct con memset, ecc non garantisce il valore dei bit di riempimento dopo ulteriori operazioni di scrittura ad un elemento struct, vedi: stackoverflow.com/q/52684192/689161
gengkev

4

Nota che puoi usare memcmp () su strutture non statiche senza preoccuparti dell'imbottitura, purché non inizializzi tutti i membri (contemporaneamente). Questo è definito da C90:

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


1
È stato effettivamente specificato che {0, }azzererà anche i byte di riempimento?
Alnitak,

GCC almeno azzera i byte di riempimento per le strutture parzialmente inizializzate, come dimostrato al link sopra, e stackoverflow.com/questions/13056364/… dettagli che C11 specifica quel comportamento.
pixelbeat,

1
Non molto utile in generale, perché tutte le imbottiture diventano indeterminate dopo l'assegnazione a qualsiasi membro
MM

2

Dipende se la domanda che stai ponendo è:

  1. Queste due strutture sono lo stesso oggetto?
  2. Hanno lo stesso valore?

Per scoprire se sono lo stesso oggetto, confronta i puntatori con le due strutture per l'uguaglianza. Se vuoi scoprire in generale se hanno lo stesso valore devi fare un confronto profondo. Ciò comporta il confronto di tutti i membri. Se i membri sono puntatori ad altre strutture è necessario ricorrere anche a quelle strutture.

Nel caso speciale in cui le strutture non contengono puntatori, è possibile eseguire un memcmp per eseguire un confronto bit a bit dei dati contenuti in ciascuno senza dover sapere cosa significano i dati.

Assicurati di sapere cosa significa 'uguale a' per ogni membro - è ovvio per gli inte ma più sottile quando si tratta di valori in virgola mobile o tipi definiti dall'utente.


2

memcmpnon confronta la struttura, memcmpconfronta il binario, e c'è sempre spazzatura nella struttura, quindi viene sempre fuori Falso in confronto.

Confronta elemento per elemento è sicuro e non fallisce.


1
se le variabili di 2 strutture sono inizializzate con calloc o sono impostate con 0 da memset in modo da poter confrontare le tue 2 strutture con memcmp e non c'è preoccupazione per la spazzatura della struttura e questo ti permetterà di guadagnare tempo
MOHAMED

1

Se le strutture contengono solo primitive o se sei interessato a una rigorosa uguaglianza, puoi fare qualcosa del genere:

int my_struct_cmp (const struct my_struct * lhs, const struct my_struct * rhs)
{
    return memcmp (lhs, rsh, sizeof (struct my_struct));
}

Tuttavia, se le tue strutture contengono puntatori ad altre strutture o unioni, dovrai scrivere una funzione che confronta correttamente le primitive ed effettuare chiamate di confronto con le altre strutture come appropriato.

Tieni presente, tuttavia, che avresti dovuto utilizzare memset (& a, sizeof (struct my_struct), 1) per azzerare l'intervallo di memoria delle strutture come parte dell'inizializzazione di ADT.


-1

se le variabili delle 2 strutture sono inizializzate con calloc o sono impostate con 0 dal memset in modo da poter confrontare le tue 2 strutture con memcmp e non c'è preoccupazione per la spazzatura delle strutture e questo ti permetterà di guadagnare tempo


-2

Questo esempio conforme utilizza l'estensione del compilatore #pragma pack di Microsoft Visual Studio per garantire che i membri della struttura siano impacchettati il ​​più strettamente possibile:

#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
Questo è davvero corretto. Ma nella maggior parte dei casi non vuoi che le tue strutture vengano imballate! Molte istruzioni e puntatori richiedono che i dati di input siano allineati a parole. In caso contrario, il compilatore deve aggiungere ulteriori istruzioni per copiare e riallineare i dati prima di poter eseguire l'istruzione effettiva. Se il compilatore non riallinea i dati, la CPU genererà un'eccezione.
Ruud Althuizen,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.