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

检查GUID是否为空(在C中),c,windows,C,Windows,我想检查GUID结构是否为空/所有字段是否为0。这是我写的代码: #include <windows.h> static BOOL IsEmptyGuid(const GUID * const pGuid) { return \ (pGuid->Data1 == 0) && (pGuid->Data2 == 0) && (pGuid->Data3 == 0) && #ifdef _WI

我想检查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));
}
#包括
静态BOOL IsEmptyGuid(常量GUID*常量pGuid)
{
返回\
(pGuid->Data1==0)&&
(pGuid->Data2==0)&&
(pGuid->Data3==0)&&
#ifdef_WIN64
(*(DWORD64*)pGuid->Data4==0);
#否则
(*(DWORD*)pGuid->Data4==0)和(*(DWORD*)(pGuid->Data4+4)==0);
#恩迪夫
}
/*MSDN中的GUID定义
typedef结构\u GUID{
德沃德数据1;
字数据2;
worddata3;
字节数据4[8];
}GUID;
*/
int main(){
guid1;
guid1.Data1=0;
guid1.Data2=0;
guid1.Data3=0;
memset(guid1.Data4,0x0,8);
printf(“结果:%u\n”,ismptyguid(&guid1));
}
检查字段
Data4
是否等于0的更安全的方法是迭代每个字节并检查其值。但是,我发现上面的代码更有表现力

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


谢谢大家!

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

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

  • 与对象的有效类型兼容的类型
  • 与对象的有效类型兼容的类型的限定版本
  • 一种类型,它是与数据的有效类型相对应的有符号或无符号类型 反对,
  • 一种类型,它是与的限定版本相对应的有符号或无符号类型 对象的有效类型
  • 一种聚合或联合类型,其中包括上述类型之一 成员(递归地包括子集合或包含的联合的成员),或
  • 字符类型
88)此列表的目的是指定对象可能有别名或可能没有别名的情况

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

DWORD64 * temp = (DWORD64 *)pGuid->Data4; // Allowed, but implementation defined
DWORD64 temp2 = *temp;                    // Undefined behaviour
使用loop单独检查每个元素,或与
memcmp
比较相同大小的零填充数组



如注释中所述,某些编译器允许禁用严格别名,但应避免这种情况,因为它会降低代码的可移植性,并且仍然存在潜在的对齐问题。
\u WIN64
未定义时

(*(DWORD *)pGuid->Data4 == 0) && (*(DWORD *)(pGuid->Data4 + 4) == 0);
案例-这是绝对安全和正确的-
Data4
成员与
DWORD
(4)对齐,大小为2
DWORD
2*sizeof(DWORD)==8

对于
\u WIN64
而言,
Data4
的对齐仍然
DWORD
(4),因为所有结构对齐都是4(
C\u断言(
C\u alignof(GUID)==4)
)-当code
(*(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。否则将不可能将任何二进制接口连接到一段代码


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

  • Data4
    不是指向8个连续字节的指针吗
  • Data4
    是否与GUID有8个字节的偏移量
  • Data4
    未在4字节上对齐(当然,如果pGuid本身正确 对齐)
  • 我们无法将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__
    

    @RbMm请不要在评论部分给出不正确的答案,因为它们不能被否决。@user694733-为什么您认为这是不正确的或不安全的?绝对正确。我认为它太小了,无法回答为什么不创建一个带有
    GUID\u empty={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}的空GUID
    然后使用内置函数IsEqualGUID
    ?不,我只是不知道它存在。我寻找一个函数,告诉我它是否为空。这是一个视角的改变,看看是否平等。很好,谢谢!只是不要重新发明这个轮子,使用IsEqualGUID()与GUID_NULL进行比较。@RbMm这是UB。它被定义为通过
    [[un]signed]char*
    指针重新解释其他类型,但不是反过来。(假设
    BYTE
    肯定是
    char
    类型)只要一直这样说,它就会神奇地变成现实。或者,引用一个支持你的消息来源。这无关紧要。这个答案是正确的,通过不同类型的指针访问
    char
    数组是未定义的行为。这很可能发生在MS的编译器上,也许他们不太可能改变这一点。这并不意味着它是“正确的”或好的代码。这里有一些简单的方法可以获得完全定义的行为,特别是因为它是C(不是C++),所以没有理由继续使用脆弱的代码。@RbMm您一直这样说,没有任何证据。已经一次又一次地证明,编译器可以使用别名规则来优化像您这样的无效代码。这与你模糊的“二进制兼容性”概念没有任何关系,其他人都可以在不编写根本性破坏的代码的情况下实现“二进制兼容性”。然而,还是有。取消对类型双关指针的引用会破坏别名规则。该标准明确禁止这样做。当有简单的方法以一种明确的方式来实现这一点时,你为什么会如此热情地鼓励人们编写脆弱的代码呢?这是一个迂腐的讨论。从标准pov来看,它是UB,因此不正确,但可能没有一个编译器在地址对齐时不能工作correctly@ServéLaurijssen我认为建议不要编写与语言定义相矛盾的代码,并且不需要在任何编译器上工作,特别是当