¿Cómo se comparan dos instancias de estructuras para la igualdad en el estándar C?
¿Cómo se comparan dos instancias de estructuras para la igualdad en el estándar C?
Respuestas:
C no proporciona facilidades de lenguaje para hacer esto; debe hacerlo usted mismo y comparar cada estructura miembro por miembro.
0.0, -0.0 NaN
es 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 int
con -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.
==
no funciona con estructuras (como yo), consulte stackoverflow.com/questions/46995631/…
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 calloc
o memset
el 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).
memcmp
siempre 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 memcmp
hace exactamente eso (ya sea que la memoria se borre o no).
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
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.
@Greg tiene razón en que uno debe escribir funciones de comparación explícitas en el caso general.
Es posible usar memcmp
si:
NaN
.-Wpadded
con clang para verificar esto) O las estructuras se inicializan explícitamente con memset
en la inicialización.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 NULL
punteros.
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_STRING
estructuras en Windows compararse superficial o profundamente?
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
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:
{0, }
también pondrá a cero los bytes de relleno?
Depende de si la pregunta que hace es:
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.
memcmp
no compara la estructura, memcmp
compara 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.
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.
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))) {
/* ... */
}
}