Long alignment (MSVC vs. GCC)

I am writing a C cross-platform library, but in the end I have an error in my unittests, but only on Windows machines. I tracked the problem and found that it is related to alignment of structures (I use arrays of structures to store data for several similar objects). The problem is that memset (sizeof (struct)) and structural elements in turn produce different byte-byte results, and therefore memcmp () returns a non-equal result.

Here is the code to illustrate:

#include <stdio.h> #include <string.h> typedef struct { long long a; int b; } S1; typedef struct { long a; int b; } S2; S1 s1, s2; int main() { printf("%d %d\n", sizeof(S1), sizeof(S2)); memset(&s1, 0xFF, sizeof(S1)); memset(&s2, 0x00, sizeof(S1)); s1.a = 0LL; s1.b = 0; if (0 == memcmp(&s1, &s2, sizeof(S1))) printf("Equal\n"); else printf("Not equal\n"); return 0; } 

This code with MSVC 2003 @Windows produces the following output:

 16 8 Not equal 

But the same code with GCC 3.3.6 @Linux works as expected:

 12 8 Equal 

This makes my unit testing very difficult.

Did I understand correctly that MSVC uses the size of the largest native type (long long) to determine the alignment of the structure?

Can someone give me some advice on how I can change my code to make it more resistant to this strange alignment problem? In my real code, I work with arrays of structures through shared pointers to execute memset / memcmp, and I usually don't know the exact type, I only have the value of sizeof (struct).

+1
c struct alignment cross-platform unit-testing
source share
6 answers

What we did is use the #pragma package to determine how large the objects should be:

 #pragma pack(push, 2) typedef struct { long long a; int b; } S1; typedef struct { long a; int b; } S2; #pragma pack(pop) 

If you do this, the structures will be the same size on both platforms.

+1
source share

Your expectation of unit test is incorrect. It (or the code it tests) should not check the default bytes. For byte-accurate data, the code must create a byte buffer explicitly on the stack or on the heap and fill it with excerpts from each member. Excerpts can be obtained in a CPU-independent way using the right shift operation with respect to integer values ​​and outputting the result as a byte, for example (unsigned char).

By the way, your snippet writes past s2. You can fix it by changing this

 memset(&s2, 0x00, sizeof(S1)); s1.a = 0LL; s1.b = 0; if (0 == memcmp(&s1, &s2, sizeof(S1))) 

 memset(&s2, 0x00, sizeof(S2)); s1.a = 0LL; s1.b = 0; if (0 == memcmp(&s1, &s2, sizeof(S2))) 

but the result is technically "undefined" because alignment of elements in structures is compiler specific.

+4
source share

GCC Guide:

Note that alignment of any given type of structure or union is required by ISO C to be at least an ideal multiple of the lowest common multiple alignment of all members of the structure or union in question.

In addition, this usually introduces a padding element (i.e., filler bytes to align the structure). You can use #pragma with the packed argument. Note. #pragma NOT a portable way of working. Unfortunately, this also applies to the only way to work in your case.

References: Here is the GCC for leveling the structure. MSDN alignment structure.

+2
source share

You can do something like

 #ifdef _MSC_VER #pragma pack(push, 16) #endif /* your struct defs */ #ifdef _MSC_VER #pragma pack(pop) #endif 

to give compiler directive forced alignment

Or go into the project settings and change the default alignment [in the Code Generation section]

+1
source share

Please note that this is not a β€œweird” alignment problem. MSVC decided to make sure that the structure is aligned on the 64-bit boundary, since it has a 64-bit element, therefore it adds some additions at the end of the structure to ensure that the arrays of these objects will be correctly aligned on each element. I am really surprised that GCC is not doing the same.

I am curious that you are testing on a module what gets into this problem - most of the time alignment of structure members is optional if you do not need to comply with the binary file format or wired protocol or you really need to reduce the memory used by the structure (especially for embedded systems) . Not knowing what you are trying to do in your tests, I don’t think you can give a good suggestion. Packing the structure may be a solution, but it brings some cost-effectiveness (especially on platforms other than Intel) and portability (how the packaging structure is configured can differ from compiler and compiler). This may not matter to you, but it may be the best way to solve the problem in your case.

+1
source share

Filling the structure for 64-bit values ​​is different for different compilers. I have seen differences between even between gcc goals.

Note that explicitly indenting to 64-bit alignment will only hide the problem. It will return if you start naively nested structures, because compilers will still disagree with the natural alignment of internal structures.

0
source share

All Articles