使用指向 typedef 固定长度数组的指针执行 memcpy 时是否需要取消引用?为什么或者为什么不?

Is there a need to dereference when performing a memcpy using pointer to typedef fixed length array? Why or why not?

好的 - 所以我会先说我不完全确定如何描述这个问题和我目前的困惑,所以我会尽力提供示例。

问题

在 memcpy 调用(在下面的“上下文”中显示)中使用 typedef 定长数组的两种方法中哪一种是正确的?或者它们是等价的?

(我开始认为它们是等价的——下面“注释”下的一些实验)。

上下文

考虑以下 typedef typedef uint8_t msgdata[150]; 和库接口 const msgdata* IRead_GetMsgData (void); .

在我的代码中,我使用 IRead_GetMsgData 并将结果 memcpy 到另一个 uint8_t 缓冲区(下面的人为示例)。

//Included from library:
//typedef uint8_t msgdata[150];
//const msgdata* IRead_GetMsgData (void);

uint8_t mBuff[2048];
void Foo() {
    const msgdata* myData = IRead_GetMsgData();
    if(myData != nullptr) {
        std::memcpy(mBuff, *myData, sizeof(msgdata));
    }
}

现在,这可以正常工作并通过我们的单元测试,但它开始了我们团队之间关于我们是否应该在这种情况下取​​消引用 myData 的讨论。事实证明,不取消引用 myData 也有效并通过了我们所有的单元测试

    std::memcpy(mBuff, myData, sizeof(msgdata)); //This works fine, too

我在编写 memcpy 调用时的想法是,因为 myData 是 msgdata* 类型,取消引用它会 return 指向的 msgdata,它是一个 uint8_t 数组。 例如

    typedef uint8 msgdata[150];
    msgdata mData = {0u};
    msgdata* pData = &mData;     
    memcpy(somePtr, pData, size);  //Would expect this to fail - pData isn't the buffer mData.    
    memcpy(somePtr, *pData, size); //Would expect this to work - dereferencing pData returns the buffer mData
    memcpy(somePtr, mData, size); //Would expect this to work - mData is the buffer, mData ==&mData[0]

我尝试搜索类似问题的讨论,但尚未找到任何相关的内容:

该列表中的最后一个与我最相关,因为接受的答案很好地说明了(强调我的)

[this form of typedef is] probably a very bad idea

现在我试图了解实际发生的事情,我完全同意!尤其是因为它隐藏了您实际尝试使用的类型...

备注

所以在我们开始思考这个问题之后,我做了一些实验:

typedef uint8_t msgdata[150];
msgdata data = {0};
msgdata* pData = &data;
int main() {
    printf("%p\n", pData);
    printf("%p\n", *pData);
    printf("%p\n", &data);
    printf("%p\n", data);
    
    return 0;
}
Outputs:
0x6020a0 
0x6020a0 
0x6020a0 
0x6020a0

如果我扩展它以包含一个合适的数组 arr 和一个定义的大小值 size,我可以使用各种 memcpy 调用,例如

    std::memcpy(arr, data, size);
    std::memcpy(arr, pData, size);
    std::memcpy(arr, *pData, size);

它们的行为都一样,让我相信它们是等价的。 我了解第一个和最后一个版本(data*pData),但我仍然不确定 pData 版本发生了什么...

在我看来,这段代码是完全错误的。我也接受另一种观点“代码非常具有误导性”

//Included from library:
//typedef uint8_t msgdata[150];
//const msgdata* IRead_GetMsgData (void);

uint8_t mBuff[2048];
void Foo() {
    const msgdata* myData = IRead_GetMsgData();
    if(myData != nullptr) {
        std::memcpy(mBuff, *myData, sizeof(msgdata));
    }
}

当您取消引用 *myData 时,您误导了 reader。显然,memcpy 需要一个指向 msgdata 的指针,因此不需要取消引用星号。 myData 已经是一个指针。引入额外的取消引用会破坏代码。

但它没有...为什么?

这就是您具体用例的切入点。typedef uint8_t msgdata[150]; msgdata 是一个退化为指针的数组。所以,*msgdata 是数组,数组是(衰减到)指向其开头的指针。

所以,你可能会争辩说:没什么大不了的,我可以把多余的 * 留在里面,对吧?

没有.

因为总有一天,有人会把代码改成:

class msgdata
{
    int something_super_useful;
    uint8_t msgdata[150];
};

在这种情况下,编译器会捕获它,但一般来说,间接级别的错误可能会编译为微妙的崩溃。找到无关的 *.

将花费您数小时或数天的时间