C++ 从无符号字符数组创建 GUID

Posted

技术标签:

【中文标题】C++ 从无符号字符数组创建 GUID【英文标题】:C++ Creating GUID from unsigned char array 【发布时间】:2021-08-25 07:50:26 【问题描述】:

我有一个函数,它将一个 unsigned char 数组(正好是 16 个值)作为输入,并通过将所有值解析为十六进制并将一个 guid 格式的字符串传递给UuidFromStringA() 创建一个 GUID(来自 GUID 结构)。

我的代码如下:

GUID CreateGuid(const uint8_t* data)

    createGuidFromBufferData(hexValues, data);
    int uuidCreationReturnCode = UuidFromStringA((RPC_CSTR)hexValues, &guid);
    return guid;

            
inline void createGuidFromBufferData(char* hexValues, const uint8_t* data)

    decimalToHexadecimal(data[3], hexValues, 0);
    decimalToHexadecimal(data[2], hexValues, 2);
    decimalToHexadecimal(data[1], hexValues, 4);
    decimalToHexadecimal(data[0], hexValues, 6);
    hexValues[8] = '-';
    decimalToHexadecimal(data[5], hexValues, 9);
    decimalToHexadecimal(data[4], hexValues, 11);
    hexValues[13] = '-';
    decimalToHexadecimal(data[6], hexValues, 14);
    decimalToHexadecimal(data[7], hexValues, 16);
    hexValues[18] = '-';
    decimalToHexadecimal(data[8], hexValues, 19);
    decimalToHexadecimal(data[9], hexValues, 21);
    hexValues[23] = '-';
    decimalToHexadecimal(data[10], hexValues, 24);
    decimalToHexadecimal(data[11], hexValues, 26);
    decimalToHexadecimal(data[12], hexValues, 28);
    decimalToHexadecimal(data[13], hexValues, 30);
    decimalToHexadecimal(data[14], hexValues, 32);
    decimalToHexadecimal(data[15], hexValues, 34);


inline void decimalToHexadecimal(uint8_t decimalValue, char* outputBuffer, int currentIndex)

    const char hexValues[] = "0123456789abcdef";
    outputBuffer[currentIndex] = hexValues[decimalValue >> 4];
    outputBuffer[currentIndex + 1] = hexValues[decimalValue & 0xf];

这很好用,但我想做一些更高效的事情,并使用我的输入字符数组直接创建 GUID,如下所示:

GUID CreateGuid(const uint8_t* data)

    GUID guid =  
        *reinterpret_cast<const unsigned long*>(data), 
        *reinterpret_cast<const unsigned short*>(data + 4), 
        *reinterpret_cast<const unsigned short*>(data + 6), 
        *reinterpret_cast<const unsigned char*>(data + 8)
    ;
    return guid;

这样做时,只设置最后8个字节中的一个,其余为0; 例如使用无符号字符数组 [38、150、233、16、43、188、117、76、 187、62、254、96、109、226、87、0]

我应该得到什么时候:

10e99626-bc2b-754c-bb3e-fe606de25700

我得到的是:

10e99626-bc2b-75dc-bb00-000000000000

【问题讨论】:

请出示minimal reproducible example 也许使用sprintf @Bodo 仍然会从我的 unsigned char 数组创建一个字符串,而不是直接使用 unsigned char 数组 【参考方案1】:

GUID 是一个具有简单复制分配的聚合。因此,您应该可以直接执行此操作。

GUID CreateGuid(const uint8_t* data)

    return *reinterpret_cast<GUID*>(data)

【讨论】:

那不是UB吗?在 C++20 中,您可以使用 std::bit_cast 而不是 reinterpret_cast 另请注意,尽管GUID 结构在填充方面经过精心设计(long, short, short, char[]),但由于潜在的填充,此方法不能保证适用于任何“微不足道”的聚合元素之间。【参考方案2】:

您不能像您尝试的那样简单地通过取消引用指向源数据的强制转换指针并将该值分配给它的第一个元素来填充 8 字符数组。正如您所注意到的,这样做只会为第一个元素分配一个值。

对于GUID结构末尾的8字符数组,可以单独初始化每个元素,扩展cast+offset方法:

GUID CreateGuid(const uint8_t* data)

    GUID guid = 
        *reinterpret_cast<const unsigned long*>(data),
        *reinterpret_cast<const unsigned short*>(data + 4),
        *reinterpret_cast<const unsigned short*>(data + 6),
         // We need to assign each of the 8 elements of the array ...
            *reinterpret_cast<const unsigned char*>(data + 8),
            *reinterpret_cast<const unsigned char*>(data + 9),
            *reinterpret_cast<const unsigned char*>(data + 10),
            *reinterpret_cast<const unsigned char*>(data + 11),
            *reinterpret_cast<const unsigned char*>(data + 12),
            *reinterpret_cast<const unsigned char*>(data + 13),
            *reinterpret_cast<const unsigned char*>(data + 14),
            *reinterpret_cast<const unsigned char*>(data + 15),
        
    ;
    return guid;

注意: 正如 cmets 中所指出的(感谢 Timo),使用这种直接初始化方法可能会引发未定义的行为,尤其是在传递的源数据缓冲区未针对对应的目的地类型,因为Strict Aliasing Rule。为避免这种情况,您应该使用memcpy 将数据从源缓冲区传输到目标结构的元素:

GUID CreateGuid(const uint8_t* data)

    GUID guid;
    std::memcpy(&guid.Data1, data, sizeof(long));
    std::memcpy(&guid.Data2, data + 4, sizeof(short));
    std::memcpy(&guid.Data3, data + 6, sizeof(short));
    std::memcpy(guid.Data4, data + 8, 8); // sizeof(char) == 1
    return guid;

此外,由于GUID 结构的定义方式是不应该在其元素之间添加填充字节,因此您可以一举复制数据:

GUID CreateGuid(const uint8_t* data)

    GUID guid;
    std::memcpy(&guid, data, sizeof(GUID)); // Assuming no padding!
    return guid;

【讨论】:

那些reinterpret_casts UB不是因为类型别名规则吗?至少是非字符的 @Timo 可能。但是,如果传递的数据作为一个整体正确对齐(即0 元素与long 正确对齐),那么应该没有问题。但你是对的 - 使用 memcpy() 而不是直接初始化会更好/更安全...... "如果传递的源数据缓冲区没有适当对齐"。我认为,严格来说,这对于 UB 案例无关紧要,因为您总是违反严格的别名规则(除非 data 指向 GUID 对象)。即使float f = 1.0; int32_t x = *reinterpret_cast&lt;int32_t*&gt;(&amp;f) 根据严格的别名也是UB,即使它们具有相同的对齐方式。 @Timo 再次编辑......现在我要去喝一些急需的咖啡! :-)

以上是关于C++ 从无符号字符数组创建 GUID的主要内容,如果未能解决你的问题,请参考以下文章

opencv c++:将图像矩阵从无符号字符转换为浮点数

Visual C++ 6.0 中“无符号字符”数组的最大允许大小是多少?

C++ - 分配一个无符号字符缓冲区,然后用一个字符串填充它

从文件中读取文本到无符号字符数组

复制无符号字符数组

C ++根据用户输入字符串长度创建二维数组