Hans Sjunnesson
Hans Sjunnesson

Reputation: 22329

How do you compare structs for equality in C?

How do you compare two instances of structs for equality in standard C?

Upvotes: 263

Views: 283034

Answers (14)

Peter Bruins
Peter Bruins

Reputation: 1

A somewhat useful; but slow method is as follows:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>


#define GENERATE_COMPARE(s) int compare_##s(s a, s b) {\
        s _a, _b; \
        bzero(&_a, sizeof(s)); \
        bzero(&_b, sizeof(s)); \
        _a = a; _b = b; \
        return bcmp(&_a,&_b,sizeof(s)); \
}

typedef struct foo { int a,b; } foo_t;

GENERATE_COMPARE(foo_t);


int main(int xc, char **ac) {
        foo_t a = { 1, 2 };
        foo_t b = { 2, 3 };
        foo_t c = { 1, 2 };

        if (compare_foo_t(a,b)) {
            printf("A and B are not equal\n");
        } else {
            printf("A and B are equal\n");
        };

        assert(0 != compare_foo_t(a,b));
        assert(0 != compare_foo_t(b,a));

        assert(0 != compare_foo_t(b,a));
        assert(0 != compare_foo_t(c,b));

        assert(0 == compare_foo_t(c,a));
        assert(0 == compare_foo_t(a,c));
}

This bypasses the issues of (non)packed structs by nulling two temporary sections of memory; copying the struct to there; and then compare.

Upvotes: -1

ardnew
ardnew

Reputation: 2086

For many cases on embedded systems, structs are frequently used for their bitfield capability in particular. A good (safe) option in this case is to union your struct anonymously with a fixed-width type whose equality operator is well defined.

  • Using bitfields isn't required to achieve this, of course. It's just that, in their case, you will most likely have some fixed-width integer corresponding to the struct in which they are defined.
  • Breaking up a struct into multiple unions would be defeating any convenience you're trying to achieve.

You can try this example in your browser:

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

// Given the struct you want to compare:
//
//   | typedef struct {
//   |    uint8_t  a : 1;
//   |    uint16_t b : 9;
//   |    uint8_t  c : 3;
//   |    uint8_t  _ : 2;
//   |    uint8_t  d : 1;
//   | } data;
//
// Embed it anonymously in a union:
typedef union {
  uint16_t u16;
  struct {
    uint8_t  a : 1;
    uint16_t b : 9;
    uint8_t  c : 3;
    uint8_t  _ : 2;
    uint8_t  d : 1;
  };
} data;

// Code that previously referred to members of struct data:
//
//   | data d;  // struct
//   | d.a = 0; // immediate field a of d.
//
// Can continue using identical syntax without change:
//
//   | data d;  // union
//   | d.a = 0; // nested field a of anonymous struct in d.

// Return 0 if a == b, -1 if a < b, +1 if a > b.
int  data_cmp(const void *a, const void *b);

int main(int argc, char *argv[]) {
  data d[] = { 
    { .a = 1, .b = 300, .c = 4, .d = 0 },
    { .a = 1, .b = 300, .c = 4, .d = 0 },
    { .a = 0, .b = 300, .c = 4, .d = 0 },
    { .a = 1, .b = 500, .c = 2, .d = 0 },
  };
  size_t n = sizeof(d) / sizeof(*d);

  // You can use any comparison operator on the value unioned 
  // with your struct.
  if (d[0].u16 == d[1].u16) {
    // You can also make use of ordinary library functions.
    qsort(d, n, sizeof(data), data_cmp);
  }

  // When not testing for equality (e.g., sorting), be aware of 
  // byte ordering!
  for (size_t i = 0; i < n; ++i) { 
    printf("d[%d]: { %d, %d, %d, %d }\n", 
      i, d[i].a, d[i].b, d[i].c, d[i].d);
  }
}

int data_cmp(const void *a, const void *b) { 
  return ((const data *)a)->u16 - ((const data *)b)->u16; 
}

Upvotes: -1

Angelo Dureghello
Angelo Dureghello

Reputation: 330

Question is related to C, not gcc.

The fact is that several C compilers exists, and not all of them are nearly adherent to the standards. And also, several different architectures exists around, with different mem alignments for structures.

To compare the structs, those should be at least "pragma packed", and properly zero-initialized.

But as said above multiple times, better to compare their members as needed.

Upvotes: 0

Demi
Demi

Reputation: 3611

@Greg is correct that one must write explicit comparison functions in the general case.

It is possible to use memcmp if:

  • the structs contain no floating-point fields that are possibly NaN.
  • the structs contain no padding (use -Wpadded with clang to check this) OR the structs are explicitly initialized with memset at initialization.
  • there are no member types (such as Windows BOOL) that have distinct but equivalent values.

Unless you are programming for embedded systems (or writing a library that might be used on them), I would not worry about some of the corner cases in the C standard. The near vs. far pointer distinction does not exist on any 32- or 64- bit device. No non-embedded system that I know of has multiple NULL pointers.

Another option is to auto-generate the equality functions. If you lay your struct definitions out in a simple way, it is possible to use simple text processing to handle simple struct definitions. You can use libclang for the general case – since it uses the same frontend as Clang, it handles all corner cases correctly (barring bugs).

I have not seen such a code generation library. However, it appears relatively simple.

However, it is also the case that such generated equality functions would often do the wrong thing at application level. For example, should two UNICODE_STRING structs in Windows be compared shallowly or deeply?

Upvotes: 10

Sufian
Sufian

Reputation: 8755

You may be tempted to use memcmp(&a, &b, sizeof(struct foo)), but it may not work in all situations. The compiler may add alignment buffer space to a structure, and the values found at memory locations lying in the buffer space are not guaranteed to be any particular value.

But, if you use calloc or memset the full size of the structures before using them, you can do a shallow comparison with memcmp (if your structure contains pointers, it will match only if the address the pointers are pointing to are the same).

Upvotes: 136

Hesham Eraqi
Hesham Eraqi

Reputation: 2542

This compliant example uses the #pragma pack compiler extension from Microsoft Visual Studio to ensure the structure members are packed as tightly as 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))) {
    /* ... */
  }
}

Upvotes: -3

MOHAMED
MOHAMED

Reputation: 43578

if the 2 structures variable are initialied with calloc or they are set with 0 by memset so you can compare your 2 structures with memcmp and there is no worry about structure garbage and this will allow you to earn time

Upvotes: -3

sergio
sergio

Reputation: 427

memcmp does not compare structure, memcmp compares the binary, and there is always garbage in the struct, therefore it always comes out False in comparison.

Compare element by element its safe and doesn't fail.

Upvotes: 1

domgblackwell
domgblackwell

Reputation: 5100

It depends on whether the question you are asking is:

  1. Are these two structs the same object?
  2. Do they have the same value?

To find out if they are the same object, compare pointers to the two structs for equality. If you want to find out in general if they have the same value you have to do a deep comparison. This involves comparing all the members. If the members are pointers to other structs you need to recurse into those structs too.

In the special case where the structs do not contain pointers you can do a memcmp to perform a bitwise comparison of the data contained in each without having to know what the data means.

Make sure you know what 'equals' means for each member - it is obvious for ints but more subtle when it comes to floating-point values or user-defined types.

Upvotes: 2

pixelbeat
pixelbeat

Reputation: 31768

Note you can use memcmp() on non static stuctures without worrying about padding, as long as you don't initialise all members (at once). This is defined by C90:

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

Upvotes: 3

Kevin S.
Kevin S.

Reputation: 891

If the structs only contain primitives or if you are interested in strict equality then you can do something like this:

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

However, if your structs contain pointers to other structs or unions then you will need to write a function that compares the primitives properly and make comparison calls against the other structures as appropriate.

Be aware, however, that you should have used memset(&a, sizeof(struct my_struct), 1) to zero out the memory range of the structures as part of your ADT initialization.

Upvotes: -1

structinator
structinator

Reputation:

You can't use memcmp to compare structs for equality due to potential random padding characters between field in structs.

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

The above would fail for a struct like this:

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

You have to use member-wise comparison to be safe.

Upvotes: 23

Ben
Ben

Reputation: 2801

If you do it a lot I would suggest writing a function that compares the two structures. That way, if you ever change the structure you only need to change the compare in one place.

As for how to do it.... You need to compare every element individually

Upvotes: 25

Greg Hewgill
Greg Hewgill

Reputation: 994371

C provides no language facilities to do this - you have to do it yourself and compare each structure member by member.

Upvotes: 243

Related Questions