¿Cómo se comparan las estructuras de igualdad en C?


Respuestas:


196

C no proporciona facilidades de lenguaje para hacer esto; debe hacerlo usted mismo y comparar cada estructura miembro por miembro.


19
si las variables de 2 estructuras se inicializan con calloc o si se configuran con 0 por memset para que pueda comparar sus 2 estructuras con memcmp y no se preocupe por la basura de la estructura y esto le permitirá ganar tiempo
MOHAMED

21
@MOHAMED Comparar campos de punto flotante con 0.0, -0.0 NaNes un problema con memcmp(). Los punteros que difieren en la representación binaria pueden apuntar a la misma ubicación (por ejemplo, DOS: seg: offset) y, por lo tanto, son iguales. Algunos sistemas tienen múltiples punteros nulos que se comparan por igual. Lo mismo para los oscuros intcon -0 y los tipos de coma flotante con codificaciones redundantes. (Intel long double, decimal64, etc.) Estos problemas no hacen diferencia si se calloc()usa o no o el relleno.
chux

2
@chux En cualquier sistema moderno de 32 o 64 bits que conozco, el único problema es con el punto flotante.
Demi

2
En caso de que se pregunte por qué ==no funciona con estructuras (como yo), consulte stackoverflow.com/questions/46995631/…
stefanct el

44
@Demi: Hoy. El décimo mandamiento para los programadores de C es: "Debes renunciar, renunciar y abjurar de la herejía vil que dice que" Todo el mundo es un VAX "...". Reemplazar esto con "Todo el mundo es una PC" no es una mejora.
Martin Bonner apoya a Monica el

110

Puede tener la tentación de usar memcmp(&a, &b, sizeof(struct foo)), pero puede no funcionar en todas las situaciones. El compilador puede agregar espacio de búfer de alineación a una estructura, y no se garantiza que los valores encontrados en las ubicaciones de memoria que se encuentran en el espacio de búfer sean de ningún valor en particular.

Pero, si usa calloco memsetel tamaño completo de las estructuras antes de usarlas, puede hacer una comparación superficial con memcmp(si su estructura contiene punteros, solo coincidirá si la dirección a la que apuntan los punteros es la misma).


19
Cerrar, porque funciona en "casi todos" los compiladores, pero no del todo. Consulte 6.2.1.6.4 en C90: "Dos valores (distintos de NaN) con la misma representación de objeto se comparan igual, pero los valores que se comparan igual pueden tener diferentes representaciones de objeto".
Steve Jessop

22
Considere un campo "BOOL". En términos de igualdad, cualquier BOOL distinto de cero es igual a cada valor BOOL distinto de cero. Entonces, mientras que 1 y 2 pueden ser VERDADEROS y, por lo tanto, iguales, memcmp fallará.
ajs410

44
@JSalazar Más fácil para ti, quizás, pero mucho más difícil para el compilador y la CPU y, por lo tanto, también mucho más lento. ¿Por qué crees que el compilador agrega relleno en primer lugar? Ciertamente no desperdiciar memoria por nada;)
Mecki

44
@Demetri: por ejemplo, los valores flotantes positivo y negativo cero se comparan igual en cualquier implementación flotante IEEE, pero no tienen la misma representación de objeto. Entonces, en realidad no debería haber dicho que funciona en "casi todos los compiladores", fallará en cualquier implementación que le permita almacenar un cero negativo. Probablemente estaba pensando en representaciones enteras divertidas cuando hice el comentario.
Steve Jessop

44
@Demetri: pero muchos contienen flotantes, y el interlocutor pregunta "¿cómo se comparan las estructuras", no "cómo se comparan las estructuras que no contienen flotantes". Esta respuesta dice que puede hacer una comparación superficial memcmpsiempre que la memoria se haya borrado primero. Lo cual está cerca de funcionar pero no es correcto. Por lo general, la pregunta tampoco define "igualdad", por lo que si lo toma como "igualdad de bytes de la representación del objeto", entonces memcmphace exactamente eso (ya sea que la memoria se borre o no).
Steve Jessop

22

Si lo haces mucho, te sugiero que escribas una función que compare las dos estructuras. De esa manera, si alguna vez cambia la estructura, solo necesita cambiar la comparación en un solo lugar.

En cuanto a cómo hacerlo ... Necesita comparar cada elemento individualmente


1
Escribiría una función separada incluso si la usara solo una vez.
Sam

18

No puede usar memcmp para comparar estructuras para la igualdad debido a posibles caracteres de relleno aleatorio entre campos en estructuras.

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

Lo anterior fallaría para una estructura como esta:

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

Debe usar la comparación entre miembros para estar seguro.


25
Es poco probable que se rellene después del doble; el carbón estará perfectamente alineado inmediatamente después del doble.
Jonathan Leffler

7

@Greg tiene razón en que uno debe escribir funciones de comparación explícitas en el caso general.

Es posible usar memcmpsi:

  • las estructuras no contienen campos de punto flotante que posiblemente sean NaN.
  • las estructuras no contienen relleno (use -Wpaddedcon clang para verificar esto) O las estructuras se inicializan explícitamente con memseten la inicialización.
  • no hay tipos de miembros (como Windows BOOL) que tengan valores distintos pero equivalentes.

A menos que esté programando para sistemas integrados (o escribiendo una biblioteca que podría usarse en ellos), no me preocuparía por algunos de los casos de esquina en el estándar C. La distinción de puntero cercano versus lejano no existe en ningún dispositivo de 32 o 64 bits. Ningún sistema no embebido que conozco tiene múltiples NULLpunteros.

Otra opción es generar automáticamente las funciones de igualdad. Si presenta sus definiciones de estructura de una manera simple, es posible utilizar un procesamiento de texto simple para manejar definiciones de estructura simples. Puede usar libclang para el caso general, ya que usa la misma interfaz que Clang, maneja todos los casos de esquina correctamente (salvo errores).

No he visto una biblioteca de generación de código de este tipo. Sin embargo, parece relativamente simple.

Sin embargo, también es el caso de que tales funciones de igualdad generadas a menudo harían algo incorrecto a nivel de aplicación. Por ejemplo, ¿deberían dos UNICODE_STRINGestructuras en Windows compararse superficial o profundamente?


2
Inicializar explícitamente las estructuras con memset, etc., no garantiza el valor de los bits de relleno después de escribir más en un elemento de estructura, consulte: stackoverflow.com/q/52684192/689161
gengkev

4

Tenga en cuenta que puede usar memcmp () en estructuras no estáticas sin preocuparse por el relleno, siempre que no inicialice todos los miembros (a la vez). Esto está definido por C90:

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


1
¿Se especifica realmente que {0, }también pondrá a cero los bytes de relleno?
Alnitak

GCC al menos pone a cero los bytes de relleno para estructuras parcialmente inicializadas como se demuestra en el enlace anterior, y stackoverflow.com/questions/13056364/… detalla que C11 especifica ese comportamiento.
pixelbeat

1
No es muy útil en general, porque todo el relleno se vuelve indeterminado al asignarlo a cualquier miembro
MM

2

Depende de si la pregunta que hace es:

  1. ¿Son estas dos estructuras el mismo objeto?
  2. ¿Tienen el mismo valor?

Para saber si son el mismo objeto, compare los punteros con las dos estructuras para la igualdad. Si desea averiguar en general si tienen el mismo valor, debe hacer una comparación profunda. Esto implica comparar a todos los miembros. Si los miembros son punteros a otras estructuras, también debe recurrir a esas estructuras.

En el caso especial donde las estructuras no contienen punteros, puede hacer un memcmp para realizar una comparación bit a bit de los datos contenidos en cada uno sin tener que saber qué significan los datos.

Asegúrese de saber lo que significa "igual" para cada miembro: es obvio para los ints pero más sutil cuando se trata de valores de punto flotante o tipos definidos por el usuario.


2

memcmpno compara la estructura, memcmpcompara el binario, y siempre hay basura en la estructura, por lo tanto, siempre sale Falso en comparación.

Compare elemento por elemento es seguro y no falla.


1
si las variables de 2 estructuras se inicializan con calloc o si se configuran con 0 por memset para que pueda comparar sus 2 estructuras con memcmp y no se preocupe por la basura de la estructura y esto le permitirá ganar tiempo
MOHAMED

1

Si las estructuras solo contienen primitivas o si está interesado en la igualdad estricta, puede hacer algo como esto:

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

Sin embargo, si sus estructuras contienen punteros a otras estructuras o uniones, deberá escribir una función que compare las primitivas correctamente y realizar llamadas de comparación contra las otras estructuras, según corresponda.

Sin embargo, tenga en cuenta que debería haber utilizado memset (& a, sizeof (struct my_struct), 1) para poner a cero el rango de memoria de las estructuras como parte de su inicialización ADT.


-1

si las variables de 2 estructuras se inicializan con calloc o se establecen con 0 por memset para que pueda comparar sus 2 estructuras con memcmp y no se preocupe por la basura de la estructura y esto le permitirá ganar tiempo


-2

Este ejemplo compatible utiliza la extensión del compilador #pragma pack de Microsoft Visual Studio para garantizar que los miembros de la estructura estén empaquetados de la manera más ajustada posible:

#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
Eso es realmente correcto. ¡Pero en la mayoría de los casos no desea que sus estructuras estén empaquetadas! Muchas instrucciones y punteros requieren que los datos de entrada estén alineados con palabras. Si no es así, entonces el compilador necesita agregar instrucciones adicionales para copiar y realinear los datos antes de que se pueda ejecutar la instrucción real. Si el compilador no realinearía los datos, la CPU lanzará una excepción.
Ruud Althuizen
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.