c - comparing two structure

Thread Starter

bug13

Joined Feb 13, 2012
2,002
Hi guys

Recently I need a neat way to compare two structures (with bit field), here is what I think it can be done:
Code:
#define SIZE 4

typedef struct{
  uint8_t s1 : 1;
  uint8_t s2 : 1;
  ....
  uint8_t s32 : 1;
}__STRUCT_FOO__

typedef union {
  __STRUCT_FOO__ foo;
  uint8_t data[SIZE];
}__UNION__FOO__;
Then I just loop through the data array, but every I add/remove a bit field, I need to adjust the SIZE to accommodate, what's the better to do it?


Thanks!
 
Last edited:

xox

Joined Sep 8, 2017
838
Hi guys

Recently I need a neat way to compare two structures (with bit field), here is what I think it can be done:
Code:
#define SIZE 4

typedef struct{
  uint8_t s1 : 1;
  uint8_t s2 : 1;
  ....
  uint8_t s32 : 1;
}__STRUCT_FOO__

typedef union {
  __STRUCT_FOO__ foo;
  uint8_t data[SIZE];
}__UNION__FOO__;
Then I just loop through the data array, but every I add/remove a bit field, I need to adjust the SIZE to accommodate, what's the better to do it?


Thanks!
Don't use bitfields. Use memcmp(). Just be sure the structures have the same alignment. (Look for pragma push/pack/pop-type statements.) Data generated on other platforms may not be compared in such a way, naturally (that's where serialization comes in).

EDIT: Oh snap, this is C. You should be able to use the equality operator comparison directly too, as in "a == b".
 
Last edited:

miniwinwm

Joined Feb 2, 2018
68
The general thinking I have come across over the last few years is don't use C bitfields at all, but rather use a plain old int, then and and or bits in this int using #define'd masks. The main reason for this is that the machine code that the compilers generate is more consistent across different compilers. Some compilers produce inefficient and slow machine code for bitfields. If the ints used as bitfields are initialised to zero at some point (to get the unused bits in a known state) then you can compare them. If you have a struct containing multiple ints used as bitfields (or anything else) then the only portable safe way to compare two structs is member by member. Using memcmp may work in a particular case, but it's bad practice, and one day will catch you out.
 
Last edited:

Thread Starter

bug13

Joined Feb 13, 2012
2,002
Don't use bitfields. Use memcmp(). Just be sure the structures have the same alignment. (Look for pragma push/pack/pop-type statements.) Data generated on other platforms may not be compared in such a way, naturally (that's where serialization comes in).

EDIT: Oh snap, this is C. You should be able to use the equality operator comparison directly too, as in "a == b".
You mean I can compare structure with "a==b"???

Sorry, I only understand don't use bitfields and use memcmp(), then I am lost...
 

Thread Starter

bug13

Joined Feb 13, 2012
2,002
The general thinking I have come across over the last few years is don't use bitfields at all, but rather use a plain old int, then and and or bits in this int using #define'd masks. The main reason for this is that the machine code that the compilers generate is more consistent across different compilers. Some compilers produce inefficient and slow machine code for bitfields. If the ints containing the bitfields are initialised to zero at some point (to get the unused bits in a known state) then you can compare them. If you have a struct containing multiple ints used as bitfields (or anything else) then the only portable safe way to compare two structs is member by member. Using memcmp may work in a particular case, but it's bad practice, and one day will catch you out.
I don't need to worry about portable at this stage, we always use the same compiler (XC8 from Microchip). So I am safe to use bitfields??
 

miniwinwm

Joined Feb 2, 2018
68
EDIT: Oh snap, this is C. You should be able to use the equality operator comparison directly too, as in "a == b".
You mean I can compare structure with "a==b"???
No, you can't compare structs like that in C.

Sorry, I only understand don't use bitfields and use memcmp(), then I am lost...
Well, exactly. You can use memcmp to compare structs only if you've got the alignment right, you do some weasel-word compiler specific stuff, and there's an R in the month and it's not a leap year. So, in a word, don't; memcmp is poor practice to compare structs and will fail any and every coding standard and commercial code review.
 
Last edited:

miniwinwm

Joined Feb 2, 2018
68
I don't need to worry about portable at this stage, we always use the same compiler (XC8 from Microchip). So I am safe to use bitfields??
If they work for you and you are familiar and happy with them, then use them; you'll be fine. They work. However, silicon vendors are moving away from them in their driver code (which is what I develop) because that code is widely used and some coding standards prevent their use due to performance and portability problems. They are not the best bit of the C language.
 

miniwinwm

Joined Feb 2, 2018
68
If I have to use bitfields, how's my original solution? I find putting all the settings/flags in one variable is very useful.
If you have 31 single bit C bitfields with a union with 4 8 bit ints (as in your structure) what will be in that unused bit in the union? If used as a local variable will the compiler initialise it to a constant value if you don't (so that a comparison would work) or would it contain a random value (that would fail a comparison even if the used 31 bits are identical). I don't know the answer; I don't know if the C language specification specifies it, or if all compilers do the same. It might work always, it might work until the day it doesn't, or it might work with one compiler but not another. Because of these unknowns it's probably not a good idea to use it.

Something else to get into the habit of considering when writing code commercially is maintenance. Commercial code often has a life of years or decades and will be fiddled with and hacked about by people other than the author in years to come who won't know what the original author knows. Your example may work with 32 bitfields, but may not with 31 as described above. You might write code with 32 bits, years down the line someone removes 1, and bang, it breaks. This may not be a concern for a student project now, but if you can get into the habit of good practice writing simple maintainable code and demonstrate that it's something you are aware of at an interview - you'll get the job :)
 
Last edited:

Thread Starter

bug13

Joined Feb 13, 2012
2,002
If they work for you and you are familiar and happy with them, then use them; you'll be fine. They work. However, silicon vendors are moving away from them in their driver code (which is what I develop) because that code is widely used and some coding standards prevent their use due to performance and portability problems. They are not the best bit of the C language.
So I understand you use bit mask to set/clear a bit in a byte. Say I have a application need 80 bool settings, in this case I will need 10 uint8_t variables.

Like:
Code:
uint8_t settings1;
uint8_t settings2;
....
Is there a way to group them together like structure, to make them more readable in C??
 

xox

Joined Sep 8, 2017
838
No, you can't compare structs like that in C.
Doh, right...that's C++. Sorry!

So, in a word, don't; memcmp is poor practice to compare structs and will fail any and every coding standard and commercial code review.
Ha! You say that from experience I'm sure. Using memcmp is perfectly fine if used correctly. And while not completely standardized it's fairly easy to take care of structure packing in a fairly portable way.
 

Thread Starter

bug13

Joined Feb 13, 2012
2,002
If you have 31 single bit C bitfields with a union with 4 8 bit ints (as in your structure) what will be in that unused bit in the union? If used as a local variable will the compiler initialise it to a constant value if you don't (so that a comparison would work) or would it contain a random value (that would fail a comparison even if the used 31 bits are identical). I don't know the answer; I don't know if the C language specification specifies it, or if all compilers do the same. It might work always, it might work until the day it doesn't, or it might work with one compiler but not another. Because of these unknowns it's probably not a good idea to use it.

Something else to get into the habit of considering when writing code commercially is maintenance. Commercial code often has a life of years or decades and will be fiddled with and hacked about by people other than the author in years to come who won't know what the original author knows. Your example may work with 32 bitfields, but may not with 31 as described above. You might write code with 32 bits, years down the line someone removes 1, and bang, it breaks. This may not be a concern for a student project now, but if you can get into the habit of good practice writing simple maintainable code and demonstrate that it's something you are aware of at an interview - you'll get the job :)
Thanks for the advice :)
 

miniwinwm

Joined Feb 2, 2018
68
Doh, right...that's C++. Sorry!



Ha! You say that from experience I'm sure.
Everything I write has to get through friggin flippin infuriating MISRA checks for a start! It whinges about a value of zero not having a U on the end of it to show that it's unsigned. Well honestly! Who on earth dreamed up that rule? :)
 

Thread Starter

bug13

Joined Feb 13, 2012
2,002
Everything I write has to get through friggin flippin infuriating MISRA checks for a start! It whinges about a value of zero not having a U on the end of it to show that it's unsigned. Well honestly! Who on earth dreamed up that rule? :)
Sounds like the work you do is very safety critical!!
 

miniwinwm

Joined Feb 2, 2018
68
So I understand you use bit mask to set/clear a bit in a byte. Say I have a application need 80 bool settings, in this case I will need 10 uint8_t variables.

Like:
Code:
uint8_t settings1;
uint8_t settings2;
....
Is there a way to group them together like structure, to make them more readable in C??
Nothing to stop you grouping them in a struct. You could use 32 bit ints, then only need 3, but that may have a performance hit on an 8 bit processor. A general idea is that lots of repeated simple clear code is easier to understand, debug and maintain than a small amount of obscure clever code. In the early days of C, programmers seemed to delight in getting as much done with the least possible source code by using obscure oh-so-clever features of the language (look at some of the standard library implementation for examples of that), but thinking has changed, and KISS is the motto now.
 

xox

Joined Sep 8, 2017
838
Everything I write has to get through friggin flippin infuriating MISRA checks for a start! It whinges about a value of zero not having a U on the end of it to show that it's unsigned. Well honestly! Who on earth dreamed up that rule? :)
Okay...and the solution is a member for member check or what?
 

xox

Joined Sep 8, 2017
838
Nothing to stop you grouping them in a struct. You could use 32 bit ints, then only need 3, but that may have a performance hit on an 8 bit processor. A general idea is that lots of repeated simple clear code is easier to understand, debug and maintain than a small amount of obscure clever code. In the early days of C, programmers seemed to delight in getting as much done with the least possible source code by using obscure oh-so-clever features of the language (look at some of the standard library implementation for examples of that), but thinking has changed, and KISS is the motto now.
Which is precisely why you use memcmp!
 

miniwinwm

Joined Feb 2, 2018
68
Sounds like the work you do is very safety critical!!
Writing device driver libraries for a silicon vendor we don't know the final application; it could be anything from a hobbyist in his back bedroom doing it for the fun of it, or it could be a sensor feeding into the auto-landing system bringing down an Airbus in fog. The code has to meet all sorts of standards, is rigorously tested, but bugs still get through. One was reported to me today where doing a device driver DeInit before an Init caused the Init to fail. Oops!
 
Last edited:

miniwinwm

Joined Feb 2, 2018
68
Which is precisely why you use memcmp!
We'll have to agree to disagree on this one. The code is risky, the outcome is compiler and data type specific, or requires non-standard language extensions to make it work, which in my line of work developing widely distributed source, is an absolute no-no. Member by member comparison always works and has no risk.
 
Last edited:

miniwinwm

Joined Feb 2, 2018
68
Okay...and the solution is a member for member check or what?
Yes. It always works. Every compiler, every processor, every data type. memcmp can fail for floats as an example (even though this thread is not about floats) because some identical float values have multiple bit representations.

The float comparison problem is an excellent example of why it is risky. It works almost all the time so passes all the unit tests, goes out into the field in umpteen million devices. Then reports trickle in of unexplained failures which can't be reproduced in the lab. The customer starts getting agitated, accusations and blame flies between the supplier and customer, each blames the other, nothing can be proven either way - then someone spots the problem by chance. A test is devised than proves where the problem lies, the supplier owns up, the litigation starts, the lawyers in sharp suits start appearing in the office asking awkward questions, you hope nobody was injured or died, you have to give evidence in court...

This may sound far fetched and a long way away from a question about a student project, but I've been there, and getting into the habit of writing safe clear maintainable reliable code can never start too soon.
 
Last edited:
Top