//
you're reading...
Programming Issues/Tips

Structure Member Pack Alignment

1. Introduction.

1.1 A few days ago someone from the Visual C# General MSDN Forum asked a question concerning the importance of structure member pack alignment. He was developing a C# application that calls a C++ API. A structure is passed between the 2 sets of codes. He wondered whether it was really essential to specify the structure member pack alignment.

1.2 This blog discusses some of the importance of specifying the struct packing. A set of example codes that demonstrates the consequences of mismatch will also be given (see section 3).

2. Structure Member Packing.

2.1 It is an important part of defining a structure especially when interop marshaling is involved.

2.2 Struct member alignment directly affects the offset of each field of the structure.

2.3 A mismatch of alignment between 2 components that share a common structure will result in the appearance of garbage data. Exceptions may also occur if memory addresses are involved.

2.4 The forum member also asked the following question :

Could absence of it (the pack specification) cause some problems when compiling on other machines? (32bit vs 64bit vs …)

2.5 My answer : it is the mismatch of struct member alignment (from whatever problem source) that results in problems.

2.6 The absence of pack specifications will result in the use of the default pack alignment. This default depends on the language used and the compiler used for that language. Some compilers do not provide the option of setting it (e.g. Visual Basic 6.0) and enforces a default setting.

2.7 The bitness of an Operating System will affect the size of certain data types (e.g. the integer). It does eventually affect struct member alignment and would contribute to problems only if it leads to the mismatch of data types across components which have been written in different languages.

3. Example Codes.

3.1 Let’s say we have the following C# structure :

[StructLayout(LayoutKind.Sequential, Pack = 1, CharSet=CharSet.Ansi)]
public struct TestStruct01
{
  public char m_ch;
  public Int32 m_int;
  public double m_double;
}

3.2 And we have the following C++ equivalent of the structure :

#pragma pack (4)
struct TestStruct01
{
  char  m_ch;
  int  m_int;
  double m_double;
};

3.3 The C# structure is declared with struct member pack alignment of 1. The C++ structure is declared with packing of 4. You can use the following C++/C# code combination to see the effects of the mismatch :

C++ API.

#include "stdafx.h"
#include
#include 

#pragma pack (4)
struct TestStruct01
{
  char  m_ch;
  int  m_int;
  double m_double;
};

void __stdcall TestAPI(/*[in]*/ TestStruct01 test_struct_01)
{
  size_t stStructSize = sizeof(test_struct_01);

  printf("sizeof(test_struct_01) in C++   : [%d].\r\n", stStructSize);

  printf("Offset of TestStruct01.m_ch     : [%d].\r\n", offsetof(TestStruct01, m_ch));
  printf("Offset of TestStruct01.m_int    : [%d].\r\n", offsetof(TestStruct01, m_int));
  printf("Offset of TestStruct01.m_double : [%d].\r\n", offsetof(TestStruct01, m_double));

  printf ("test_struct_01.m_ch      : [%c].\r\n", test_struct_01.m_ch);
  printf ("test_struct_01.m_int     : [%d].\r\n", test_struct_01.m_int);
  printf ("test_struct_01.m_double  : [%f].\r\n", test_struct_01.m_double);
}

The above code is then compiled into a DLL (TestDLL01.dll) which exports TestAPI().

C# Client

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace CSConsoleClient01
{
  class Program
  {
    [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet=CharSet.Ansi)]
    public struct TestStruct01
    {
      public char m_ch;
      public Int32 m_int;
      public double m_double;
    }

    [DllImport(@"TestDLL01.dll", CallingConvention = CallingConvention.StdCall)]
    public static extern void TestAPI([In] TestStruct01 test_struct_01);

    static void TestAPI01()
    {
      TestStruct01    test_struct_01 = new TestStruct01();
      Int32 iSize = Marshal.SizeOf(test_struct_01);

      Console.WriteLine("Size of TestStruct01 in C#      : [{0:D}].", iSize);

      test_struct_01.m_ch = 'A';
      test_struct_01.m_int = 100;
      test_struct_01.m_double = 1.123;

      TestAPI(test_struct_01);
    }

    static void Main(string[] args)
    {
      TestAPI01();
    }
  }
}

3.4 When the above C# application is run, the following output will be displayed :

Size of TestStruct01 in C#      : [13].
sizeof(test_struct_01) in C++   : [16].
Offset of TestStruct01.m_ch     : [0].
Offset of TestStruct01.m_int    : [4].
Offset of TestStruct01.m_double : [8].
test_struct_01.m_ch      : [A].
test_struct_01.m_int     : [377957120].
test_struct_01.m_double  : [0.000000].

3.5 As can be seen, the size of TestStruct01 in C# is of size 13 and that in C++ is 16. There is clearly a mismatch in terms of size.

3.6 The offset of each structure field is badly misaligned in the C++ code. As far as the C# program is concerned, the following is the offset of each field in its TestStruct01 :

Offset of TestStruct01.m_ch        : [0].
Offset of TestStruct01.m_int        : [1].
Offset of TestStruct01.m_double : [5].

Hence the C++ program displays garbage data for the “m_int” and “m_double” fields.

3.7 Try changing the packing alignment for either the C++ or the C# code so that the struct alignments for both match. Run the C# program again and you will see proper output.

4. References.

4.1 For more information on struct member packing alignment, pls refer to :

http://en.wikipedia.org/wiki/Data_structure_alignment

Advertisements

About Lim Bio Liong

I've been in software development for nearly 20 years specializing in C , COM and C#. It's truly an exicting time we live in, with so much resources at our disposal to gain and share knowledge. I hope my blog will serve a small part in this global knowledge sharing network. For many years now I've been deeply involved with C development work. However since circa 2010, my current work has required me to use more and more on C# with a particular focus on COM interop. I've also written several articles for CodeProject. However, in recent years I've concentrated my time more on helping others in the MSDN forums. Please feel free to leave a comment whenever you have any constructive criticism over any of my blog posts.

Discussion

No comments yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: