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

时间:2017-07-31 08:45:16

标签: c windows

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

$this['settings'] = function () use ($userSettings, $defaultSettings) {
        return new Collection(array_merge($defaultSettings, $userSettings));
    };

检查字段#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)); } 是否等于0的更安全的方法是迭代每个字节并检查它的值。但是,我发现上面的代码更具表现力。

我想知道,这是对的吗?这样安全吗?

谢谢!

2 个答案:

答案 0 :(得分:3)

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

  

对象的存储值只能由具有其中一个的左值表达式访问   以下类型: 88)

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

88)此列表的目的是指定对象可能存在或不存在别名的情况。

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

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

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

如评论中所述,某些编译器允许禁用严格别名,但应该避免这种情况,因为它会降低代码的可移植性,并且您仍然存在潜在的对齐问题。

答案 1 :(得分:-5)

未定义_WIN64

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

案例 - 绝对安全且正确 - Data4成员与DWORD(4)对齐,且大小为2 DWORD2 * sizeof(DWORD) == 8

_WIN64对齐Data4仍为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与GUID没有8个字节的偏移量?
  3. Data4未对齐4字节(当然,如果pGuid本身正确的话 对齐)?
  4. 我们无法检查8字节内存(在DWORD上对齐)为2 DWORD
  5. 以及来自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__