memcpy 启动索引真的需要吗?
memcpy start index really needed?
问题是当我们使用 memcpy()
复制任何字节数组时,我们应该显式声明目标缓冲区的起始(第 0 个)索引还是简单地提及它就足够了。让我展示我在说什么的例子。假设我们正在尝试将源缓冲区复制到目标缓冲区的开头。
BYTE *pucInputData; // we have some data here
BYTE ucOutputData[20] = {0};
代码 1
memcpy((void*)&ucOutputData, (void*)pucInputData, 20);
代码 2
memcpy((void*)&ucOutputData[0], (void*)pucInputData, 20);
在你的例子中,考虑到这是一个 C 代码片段,ucOutputData
是一个数组
memcpy(ucOutputData, pucInputData, 20);
memcpy(&ucOutputData[0], pucInputData, 20);
两者相同并且可以互换使用。数组的名称实质上为您提供了数组中第一个元素的地址。
现在,根据下面评论中非常有用的讨论,值得一提的是,
memcpy(&ucOutputData, pucInputData, 20);
也会在这里完成这项工作,但是数组名称和的用法之间存在根本区别数组名称的地址。考虑问题中的示例,对于 BYTE ucOutputData[20]
、
这样的定义
ucOutputData
指向20BYTE
个数组的首元素地址。
&ucOutputData
是一个指向20个BYTE
数组的指针。
所以,它们是不同的类型,C 尊重变量的类型。因此,为避免任何可能的误用和误解,推荐和安全的使用方法是前两个表达式之一。
FWIW,真的不需要这里的演员表。任何指针类型都可以隐式安全地转换为 C 中的 void *
。
因为表达式 &array[0]
与 array
相同,并且因为任何指针都可以隐式转换为 void*
,所以您应该这样做:
memcpy(ucOutputData, pucInputData, 20);
此外,由于您正在覆盖整个 ucOutputData
,因此不需要将其内容置零,因此可以删除初始化程序:
BYTE ucOutputData[20]; // no "= {0}" part
原生数组可以在不进行转换的情况下衰减为指针,因此在下面的代码片段中,对 p
的三个赋值都会产生相同的值; p
将指向数组的开头。不需要显式转换,因为转换为 void*
是隐式的。
typedef char BYTE;
BYTE ucOutputData[20] = {0};
void *p = &ucOutputData;
p = ucOutputData;
p = &ucOutputData[0];
不,你的两个例子都不是最优的。
请记住,C 中的所有数据指针都可以在不丢失信息的情况下转换 to/from void *
(这是 memcpy()
的第一个参数的类型),并且不需要强制转换这样做。
另请记住,在许多情况下,数组名称的计算结果为第一个元素的地址,例如此处。
还请记住尽可能使用 sizeof
,不要在不必要时引入文字常量。
所以,副本应该是:
memcpy(ucOutputData, pucInputData, sizeof ucOutputData);
请注意,我们使用不带括号的 sizeof
,它不是函数。我们还在 destination 缓冲区上使用它,这似乎是更安全的选择。
问题是当我们使用 memcpy()
复制任何字节数组时,我们应该显式声明目标缓冲区的起始(第 0 个)索引还是简单地提及它就足够了。让我展示我在说什么的例子。假设我们正在尝试将源缓冲区复制到目标缓冲区的开头。
BYTE *pucInputData; // we have some data here
BYTE ucOutputData[20] = {0};
代码 1
memcpy((void*)&ucOutputData, (void*)pucInputData, 20);
代码 2
memcpy((void*)&ucOutputData[0], (void*)pucInputData, 20);
在你的例子中,考虑到这是一个 C 代码片段,ucOutputData
是一个数组
memcpy(ucOutputData, pucInputData, 20);
memcpy(&ucOutputData[0], pucInputData, 20);
两者相同并且可以互换使用。数组的名称实质上为您提供了数组中第一个元素的地址。
现在,根据下面评论中非常有用的讨论,值得一提的是,
memcpy(&ucOutputData, pucInputData, 20);
也会在这里完成这项工作,但是数组名称和的用法之间存在根本区别数组名称的地址。考虑问题中的示例,对于 BYTE ucOutputData[20]
、
ucOutputData
指向20BYTE
个数组的首元素地址。&ucOutputData
是一个指向20个BYTE
数组的指针。
所以,它们是不同的类型,C 尊重变量的类型。因此,为避免任何可能的误用和误解,推荐和安全的使用方法是前两个表达式之一。
FWIW,真的不需要这里的演员表。任何指针类型都可以隐式安全地转换为 C 中的 void *
。
因为表达式 &array[0]
与 array
相同,并且因为任何指针都可以隐式转换为 void*
,所以您应该这样做:
memcpy(ucOutputData, pucInputData, 20);
此外,由于您正在覆盖整个 ucOutputData
,因此不需要将其内容置零,因此可以删除初始化程序:
BYTE ucOutputData[20]; // no "= {0}" part
原生数组可以在不进行转换的情况下衰减为指针,因此在下面的代码片段中,对 p
的三个赋值都会产生相同的值; p
将指向数组的开头。不需要显式转换,因为转换为 void*
是隐式的。
typedef char BYTE;
BYTE ucOutputData[20] = {0};
void *p = &ucOutputData;
p = ucOutputData;
p = &ucOutputData[0];
不,你的两个例子都不是最优的。
请记住,C 中的所有数据指针都可以在不丢失信息的情况下转换 to/from void *
(这是 memcpy()
的第一个参数的类型),并且不需要强制转换这样做。
另请记住,在许多情况下,数组名称的计算结果为第一个元素的地址,例如此处。
还请记住尽可能使用 sizeof
,不要在不必要时引入文字常量。
所以,副本应该是:
memcpy(ucOutputData, pucInputData, sizeof ucOutputData);
请注意,我们使用不带括号的 sizeof
,它不是函数。我们还在 destination 缓冲区上使用它,这似乎是更安全的选择。