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

问题描述

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

问题

在 memcpy 调用中使用 typedef 固定长度数组的两种方法中哪一种是正确的?或者它们是等价的吗?

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

语境

考虑以下 typedeftypedef 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* 类型,取消引用它会返回指向的 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]

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

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

[这种形式的 typedef 是] 可能是一个非常糟糕的主意

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

笔记

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

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版本发生了什么......

标签: c++arrayspointerstypedefdereference

解决方案


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

//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,您会误导读者。显然, memcpy 需要一个指向 a 的指针msgdata,因此不需要取消引用星号。myData已经是一个指针。引入额外的取消引用会破坏代码。

但它没有......为什么?

那就是您的特定用例开始的地方。typedef uint8_t msgdata[150];msgdata 是一个衰减为指针的数组。所以,*msgdata是数组,而数组是(衰减为)指向其开头的指针。

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

没有

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

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

在这种情况下,编译器会捕获它,但一般来说,间接级别错误可能会编译为细微的崩溃。您需要数小时或数天才能找到无关的*.


推荐阅读