Comment comparez-vous deux instances de structures pour l'égalité dans le standard C?
Comment comparez-vous deux instances de structures pour l'égalité dans le standard C?
Réponses:
C ne fournit aucun équipement linguistique pour ce faire - vous devez le faire vous-même et comparer chaque membre de la structure membre par membre.
0.0, -0.0 NaN
est un problème avec memcmp()
. Les pointeurs qui diffèrent dans la représentation binaire peuvent pointer vers le même emplacement (par exemple DOS: seg: offset) et sont donc égaux. Certains systèmes ont plusieurs pointeurs nuls qui se comparent également. Idem pour les int
types obscur avec -0 et à virgule flottante avec les encodages redondants. (Intel long double, decimal64, etc.) Ces problèmes ne font aucune différence est calloc()
utilisé ou non ou de remplissage.
==
ne fonctionne pas avec des structures (comme moi), veuillez consulter stackoverflow.com/questions/46995631/…
Vous pouvez être tenté d'utiliser memcmp(&a, &b, sizeof(struct foo))
, mais cela peut ne pas fonctionner dans toutes les situations. Le compilateur peut ajouter un espace tampon d'alignement à une structure, et les valeurs trouvées aux emplacements de mémoire situés dans l'espace tampon ne sont pas garanties être une valeur particulière.
Mais, si vous utilisez calloc
ou memset
la taille complète des structures avant de les utiliser, vous pouvez faire une comparaison superficielle avec memcmp
(si votre structure contient des pointeurs, elle ne correspondra que si l'adresse vers laquelle les pointeurs pointent est la même).
memcmp
condition que la mémoire ait été effacée en premier. Ce qui est proche de fonctionner mais pas correct. Ofc, la question ne définit pas non plus "égalité", donc si vous la prenez pour signifier "égalité octet de la représentation de l'objet", cela memcmp
fait exactement cela (que la mémoire soit effacée ou non).
Si vous le faites beaucoup, je suggère d'écrire une fonction qui compare les deux structures. De cette façon, si jamais vous changez la structure, il vous suffit de changer la comparaison en un seul endroit.
Quant à savoir comment le faire .... Vous devez comparer chaque élément individuellement
Vous ne pouvez pas utiliser memcmp pour comparer les structures pour l'égalité en raison des caractères de remplissage aléatoires potentiels entre les champs dans les structures.
// bad
memcmp(&struct1, &struct2, sizeof(struct1));
Ce qui précède échouerait pour une structure comme celle-ci:
typedef struct Foo {
char a;
/* padding */
double d;
/* padding */
char e;
/* padding */
int f;
} Foo ;
Vous devez utiliser la comparaison par membre pour être sûr.
@Greg a raison de dire qu'il faut écrire des fonctions de comparaison explicites dans le cas général.
Il est possible d'utiliser memcmp
si:
NaN
.-Wpadded
avec clang pour vérifier cela) OU les structures sont explicitement initialisées avec memset
à l'initialisation.BOOL
) n'a de valeurs distinctes mais équivalentes.À moins que vous ne programmiez pour des systèmes embarqués (ou que vous n'écriviez une bibliothèque qui pourrait être utilisée sur eux), je ne m'inquiéterais pas de certains des cas d'angle dans la norme C. La distinction pointeur proche / éloigné n'existe sur aucun périphérique 32 ou 64 bits. Aucun système non intégré à ma connaissance ne possède plusieurs NULL
pointeurs.
Une autre option consiste à générer automatiquement les fonctions d'égalité. Si vous disposez vos définitions de structure de manière simple, il est possible d'utiliser un traitement de texte simple pour gérer des définitions de structure simples. Vous pouvez utiliser libclang pour le cas général - car il utilise le même frontend que Clang, il gère correctement tous les cas d'angle (sauf bogues).
Je n'ai pas vu une telle bibliothèque de génération de code. Cependant, cela semble relativement simple.
Cependant, il est également vrai que de telles fonctions d'égalité générées feraient souvent la mauvaise chose au niveau de l'application. Par exemple, deux UNICODE_STRING
structures dans Windows doivent-elles être comparées de manière superficielle ou profonde?
memset
, etc. ne garantit pas la valeur des bits de remplissage après une nouvelle écriture dans un élément struct, voir: stackoverflow.com/q/52684192/689161
Notez que vous pouvez utiliser memcmp () sur des structures non statiques sans vous soucier du remplissage, tant que vous n'initialisez pas tous les membres (à la fois). Ceci est défini par C90:
{0, }
mettra également à zéro tous les octets de remplissage?
Cela dépend si la question que vous posez est:
Pour savoir s'il s'agit du même objet, comparez les pointeurs aux deux structures pour l'égalité. Si vous voulez savoir en général s'ils ont la même valeur, vous devez faire une comparaison approfondie. Cela implique de comparer tous les membres. Si les membres sont des pointeurs vers d'autres structures, vous devez également revenir dans ces structures.
Dans le cas spécial où les structures ne contiennent pas de pointeurs, vous pouvez effectuer un memcmp pour effectuer une comparaison au niveau du bit des données contenues dans chacun sans avoir à savoir ce que les données signifient.
Assurez-vous que vous savez ce que «égal» signifie pour chaque membre - c'est évident pour les entiers mais plus subtil quand il s'agit de valeurs à virgule flottante ou de types définis par l'utilisateur.
memcmp
ne compare pas la structure, memcmp
compare le binaire, et il y a toujours des ordures dans la structure, donc il sort toujours faux en comparaison.
Comparez élément par élément son coffre-fort et ne manque pas.
Si les structures contiennent uniquement des primitives ou si vous êtes intéressé par une stricte égalité, vous pouvez faire quelque chose comme ceci:
int my_struct_cmp (const struct my_struct * lhs, const struct my_struct * rhs) { return memcmp (lhs, rsh, sizeof (struct my_struct)); }
Cependant, si vos structures contiennent des pointeurs vers d'autres structures ou unions, vous devrez écrire une fonction qui compare correctement les primitives et effectuer des appels de comparaison avec les autres structures, le cas échéant.
Cependant, sachez que vous auriez dû utiliser memset (& a, sizeof (struct my_struct), 1) pour remettre à zéro la plage de mémoire des structures dans le cadre de votre initialisation ADT.
Cet exemple conforme utilise l'extension de compilateur #pragma pack de Microsoft Visual Studio pour garantir que les membres de la structure sont compressés aussi étroitement que possible:
#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))) {
/* ... */
}
}