结构填充可靠性 在C和C++中,我们被教导要处理特定的编译器结构填充,所以我们避免依赖它来进行序列化之类的事情。
另一方面,每当我们链接到第三方动态库或共享对象时,我们都依赖于一致的结构填充 让我们以结构填充可靠性 在C和C++中,我们被教导要处理特定的编译器结构填充,所以我们避免依赖它来进行序列化之类的事情。,c,dll,dllimport,C,Dll,Dllimport,另一方面,每当我们链接到第三方动态库或共享对象时,我们都依赖于一致的结构填充 让我们以为例: typedef struct _SECURITY_ATTRIBUTES { DWORD nLength; LPVOID lpSecurityDescriptor; BOOL bInheritHandle; } SECURITY_ATTRIBUTES, *PSECURITY_ATTRIBUTES, *LPSECURITY_ATTRIBUTES; HANDLE WINAPI Create
为例:
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可能会取消引用\u SECURITY\u ATTRIBUTES
指针,这与应用程序的打包方式不同
- 我能解释一下为什么可以将结构指针传递给第三方DLL,而不知道它们的结构填充吗李>
- 对于动态加载的DLL/SOs(使用
或LoadLibrary
)是否也适用相同的原理李>dlopen
- 如果我们使用
,所有这些问题都会消失吗李>#pragma pack
- 如果是这样的话,
是否是结构序列化的可靠解决方案#pragma pack
- 从语言的角度来看,你是完全正确的。结构的填充和布局是一个实现细节,编译器可以自由选择
然而,在实践中,为了使编译器在任何特定平台上都有用,它必须遵循平台ABI。如果有人试图生成一个不遵循ABI平台的编译器,那将是完全无用的
因此,可以安全地假设您使用的任何编译器都将与平台ABI兼容。您将找不到任何不可行的编译器。有趣。因此,只要序列化的表单保持在同一平台上,结构实际上就可以序列化。这意味着它是可以找到的,但不能用于网络通信,因为您不能保证源/目标平台ABI与您自己的相匹配。(当然,您不能用指针序列化任何内容,但这是另一个主题)。32位和64位平台上的结构布局将有所不同。无论如何,您不希望通过网络或磁盘对结构进行blit。因此,在我看来,关于它的推理没有什么意义。为什么不将结构blit到磁盘?因为当您有多个体系结构时,它们将充满填充和结尾问题。winapi头会经历很多麻烦,以确保不会出错。在代码片段中部分可见,它不仅是void*,而且是LPVOID,不仅是unsigned,而是DWORD。等等,确定一个通用类型系统是关键。winapi中使用的许多结构都有一个字段,用于存储结构大小、额外保险和针对设计更改的弹性。安全属性没有,请注意nLength字段。是的,有很多pragma包。另一方面,每当我们链接到第三方动态库或共享对象时,我们都依赖于一致的结构填充。设计良好的界面不会依赖于结构填充的细节…@AndrewHenle这是否意味着在设计界面时,期望传递用户初始化的结构或结构指针是不好的做法?我经常看到库会为结构分配内存,然后在调用方法时期望指针作为句柄返回。@AndrewHenle在设计OS库时拒绝自己使用结构不是一个好主意idea@Stewart这是否意味着在设计界面时,期望传递用户初始化的结构或结构指针是错误的做法?如果由于不同的命令行编译器参数(如优化级别)而导致该结构的布局发生变化,这无疑是一种不好的做法。GCC非常擅长不提供此类选项。其他编译器,就不那么多了。