结构填充可靠性

Structure padding reliability

在 C 和 C++ 中,我们被教导将结构填充视为特定于编译器的,因此我们避免在序列化等事情上依赖它。

另一方面,每当我们 link 到第 3 方动态库或共享对象时,我们都依赖于一致的结构填充。

让我们以 <windows.h> 为例:

typedef struct _SECURITY_ATTRIBUTES {
  DWORD  nLength;
  LPVOID lpSecurityDescriptor;
  BOOL   bInheritHandle;
} SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES;

HANDLE WINAPI CreateThread(
  _In_opt_  LPSECURITY_ATTRIBUTES  lpThreadAttributes,
  _In_      SIZE_T                 dwStackSize,
  _In_      LPTHREAD_START_ROUTINE lpStartAddress,
  _In_opt_  LPVOID                 lpParameter,
  _In_      DWORD                  dwCreationFlags,
  _Out_opt_ LPDWORD                lpThreadId
);

我们不应该关心使用哪个编译器来创建 Kernel32.dll,但是编译器定义的结构填充意味着如果使用了不同的编译器,那么 Kernel32.dll 可能会取消引用 _SECURITY_ATTRIBUTES 指针与我们的应用程序打包它的方式不同。

从语言的角度来看,你说得很对。结构的填充和布局是一个实现细节,编译器可以自由做出自己的选择。

但是在实践中,要使编译器在任何特定平台上都有用,它必须遵守平台 ABI。如果有人试图生产不遵守平台 ABI 的编译器,那将毫无用处。

因此,您可以放心地假设您使用的任何编译器都与平台 ABI 兼容。你不会找到任何不存在的可行编译器。