检查GUID是否为空(在C中)

我想检查一个GUID结构是否为空/所有字段都是0.这是我写的代码:

#include <windows.h> static BOOL IsEmptyGuid(const GUID * const pGuid) { return \ (pGuid->Data1 == 0) && (pGuid->Data2 == 0) && (pGuid->Data3 == 0) && #ifdef _WIN64 (*(DWORD64 *)pGuid->Data4 == 0); #else (*(DWORD *)pGuid->Data4 == 0) && (*(DWORD *)(pGuid->Data4 + 4) == 0); #endif } /* GUID definition from MSDN typedef struct _GUID { DWORD Data1; WORD Data2; WORD Data3; BYTE Data4[8]; } GUID; */ int main() { GUID guid1; guid1.Data1 = 0; guid1.Data2 = 0; guid1.Data3 = 0; memset(guid1.Data4, 0x0, 8); printf("Result: %u\n", IsEmptyGuid(&guid1)); } 

检查字段Data4等于0的更安全的方法是遍历每个字节并检查其值。 但是,我发现上面的代码更具performance力。

我想知道,这是正确的吗? 安全吗?

谢谢!

代码不正确。 它打破了严格的锯齿规则(N1570§6.5p7),导致未定义的行为。

对象的存储值只能由具有以下类型之一的左值表达式访问:

  • 与对象的有效类型兼容的类型,
  • 与该对象的有效类型兼容的类型的限定版本,
  • 对应于对象的有效类型的有符号或无符号类型,
  • 类型是对应于对象的有效类型的限定版本的有符号或无符号类型,
  • 包括其成员(包括递归,子成员或包含联盟的成员)中的上述类型之一的聚合或联合类型,或者
  • 一个字符类型。

88)这个清单的目的是为了指定一个对象可以或不可以被别名的情况。

确切地说,当你使用非匹配类型解引用指针时,会发生UB:

 DWORD64 * temp = (DWORD64 *)pGuid->Data4; // Allowed, but implementation defined DWORD64 temp2 = *temp; // Undefined behaviour 

使用循环来单独检查每个元素,或者将memcmp与相同大小的零填充数组进行比较。


正如评论中指出的那样,一些编译器允许禁用严格的别名,但应该避免这种情况,因为它使得代码的可移植性降低,并且仍然存在潜在的对齐问题。

_WIN64未定义时

 (*(DWORD *)pGuid->Data4 == 0) && (*(DWORD *)(pGuid->Data4 + 4) == 0); 

案例 – 这是绝对安全和正确的 – Data4成员对齐到DWORD (4)并具有2 DWORD2 * sizeof(DWORD) == 8 )的大小

对于_WIN64_WIN64的对齐依然是DWORD (4),因为所有的结构对齐都是4( C_ASSERT(__alignof(GUID)==4) ) – 当代码(*(DWORD64 *)pGuid->Data4 == 0); 假定8字节数据对齐引用。 对于x64,通常处理器在引用未对齐的数据时不会产生异常。 然而在另一个平台上,这可以。 在任何情况下访问未对齐的数据不利于性能。 所以检查必须是:

 BOOL IsEmptyGuid(const GUID * pGuid) { return (pGuid->Data1 == 0) && (pGuid->Data2 == 0) && (pGuid->Data3 == 0) && (*(DWORD *)pGuid->Data4 == 0) && (*(DWORD *)(pGuid->Data4 + 4) == 0); } 

适用于所有平台。

和关于成员偏移,对齐等众所周知,这里不能是任何UB。 否则将不可能有任何二进制接口之间的代码


有趣的是,这里有什么不正确的:

  1. Data4不是指向8个连续字节的指针?
  2. Data4有没有8个字节偏移GUID?
  3. Data4没有对齐4个字节(当然,如果pGuid本身正确对齐)?
  4. 我们不能检查8字节的内存(对齐DWORD )为2 DWORD

和一些来自Windows头文件的代码:

 // Faster (but makes code fatter) inline version...use sparingly #ifdef __cplusplus __inline int InlineIsEqualGUID(REFGUID rguid1, REFGUID rguid2) { return ( ((unsigned long *) &rguid1)[0] == ((unsigned long *) &rguid2)[0] && ((unsigned long *) &rguid1)[1] == ((unsigned long *) &rguid2)[1] && ((unsigned long *) &rguid1)[2] == ((unsigned long *) &rguid2)[2] && ((unsigned long *) &rguid1)[3] == ((unsigned long *) &rguid2)[3]); } __inline int IsEqualGUID(REFGUID rguid1, REFGUID rguid2) { return !memcmp(&rguid1, &rguid2, sizeof(GUID)); } #else // ! __cplusplus #define InlineIsEqualGUID(rguid1, rguid2) \ (((unsigned long *) rguid1)[0] == ((unsigned long *) rguid2)[0] && \ ((unsigned long *) rguid1)[1] == ((unsigned long *) rguid2)[1] && \ ((unsigned long *) rguid1)[2] == ((unsigned long *) rguid2)[2] && \ ((unsigned long *) rguid1)[3] == ((unsigned long *) rguid2)[3]) #define IsEqualGUID(rguid1, rguid2) (!memcmp(rguid1, rguid2, sizeof(GUID))) #endif // __cplusplus #ifndef __IID_ALIGNED__ #define __IID_ALIGNED__ #ifdef __cplusplus inline int IsEqualGUIDAligned(REFGUID guid1, REFGUID guid2) { return ((*(PLONGLONG)(&guid1) == *(PLONGLONG)(&guid2)) && (*((PLONGLONG)(&guid1) + 1) == *((PLONGLONG)(&guid2) + 1))); } #else // !__cplusplus #define IsEqualGUIDAligned(guid1, guid2) \ ((*(PLONGLONG)(guid1) == *(PLONGLONG)(guid2)) && (*((PLONGLONG)(guid1) + 1) == *((PLONGLONG)(guid2) + 1))) #endif // !__cplusplus #endif // !__IID_ALIGNED__