首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在C中使用memcpy复制数组以构造,反之亦然。

在C中使用memcpy复制数组以构造,反之亦然。
EN

Stack Overflow用户
提问于 2019-09-04 11:55:15
回答 3查看 3.9K关注 0票数 4

假设我有结构:

代码语言:javascript
复制
typedef struct {
double re;
double im;
}ComplexStruct;

和数组:

typedef double ComplexArr[2];// [0] -> real, [1] -> imag

今天,我将从ComplexStruct复制到ComplexArr,反之亦然,使用简单的for循环:

代码语言:javascript
复制
//ComplexArr to  ComplexStruct
for (i = 0 ; i < NumEl; i++)
{
     ComplexStructVec[i].re = ComplexArrVec[i][0];
     ComplexStructVec[i].im = ComplexArrVec[i][1];
}

//ComplexStruct to ComplexArr
for (i = 0 ; i < NumEl; i++)
{
     ComplexArrVec[i][0] = ComplexStructVec[i].re;
     ComplexArrVec[i][1] = ComplexStructVec[i].im;
}

有办法安全地使用memcpy至少一个方向吗?还有比for循环更快的方法吗?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2019-09-04 12:11:33

编译器中的优化器应该能够很好地完成这些代码,您不需要对其进行太多的更改就可以使其优化。但是,如果要将ComplexStructVec和ComplexArrVec传递到函数中,则应该将它们标记为restrict,以便编译器知道不存在别名。如下所示:

代码语言:javascript
复制
void copy(ComplexStruct* restrict ComplexStructVec, const ComplexArr* ComplexArrVec)
{
    unsigned NumEl = 1000;
    for (unsigned i = 0 ; i < NumEl; i++)
    {
        ComplexStructVec[i].re = ComplexArrVec[i][0];
        ComplexStructVec[i].im = ComplexArrVec[i][1];
    }
}

通过这样做,您可以消除大量生成的代码,因为它不需要处理两个参数重叠的可能性。

演示:https://godbolt.org/z/F3DUaq (只需删除“限制”以查看差异)。如果NumEl小于18,它将在每次迭代时将整个事件展开为一个加载和一个存储。

票数 3
EN

Stack Overflow用户

发布于 2019-09-04 12:52:26

是的,您可以使用memcpy,但有几个注意事项:

  1. 数组和结构的布局是相同的,这意味着编译器不对齐数组中的项或结构中的项。
  2. 与结构和数组关联的内存大小相同。
  3. 您不关心其他体系结构的可移植性(这可能会改变#1和/或#2的答案)。
  4. 这不是一种理想的编程技术,因为正如上面所指出的,它有一些潜在的缺陷。

如果在完成上述操作之后,您仍然希望这样做,那么下面的代码应该可以做到这一点:

代码语言:javascript
复制
/* NOTE: sizeof(ComplexStructVec) === sizeof(ComplexArrVec) */
memcpy((void *) ComplexStructVec,
       (void *) ComplexArrVec,
       sizeof(ComplexStructVec)*NumEl);

这是因为在这两种情况下都使用向量(数组),所以只使用它们的名称就可以得到它们的地址。memcpy将目标地址和源地址定义为void *,因此我转换了参数。要复制的字节数是以字节为单位的结构或数组的大小(参见注),乘以向量中的条目数。可能不需要(void *)强制转换。它取决于编译器、语言标准级别和其他编译时间限定符。

还要注意,我故意没有返回值的位置,返回值是指向目标的指针。如果您需要此信息,请小心,因为将其保存到ComplexStructVec可能会导致编译器(或更糟的是运行时)问题,这取决于编译器或运行时如何分配它。

一个更完整的例子:

代码语言:javascript
复制
void copy(ComplexStruct* ComplexStructVec, ComplexArr* ComplexArrVec)
{
    unsigned NumEl = 1000;
    memcpy(ComplexStructVec, ComplexArrVec, sizeof(ComplexStruct)*NumEl);
}
票数 2
EN

Stack Overflow用户

发布于 2019-09-04 14:39:14

最可移植的方法是循环,如您的示例所示。这有时被称为结构的序列化/反序列化。

结构的问题是,它们不能保证具有一致的内存布局,就像数组一样。为了避免对齐问题,编译器可以任意添加填充字节。对于仅由8字节double组成的结构,填充是非常不可能的。但从形式上讲,它并不是便携的。

但是,您可以相当安全地执行以下操作:

代码语言:javascript
复制
_Static_assert(sizeof(ComplexStruct) == sizeof(double[2]), 
                 "Weird systems not supported");

ComplexStruct cs;
double arr[2];
memcpy(&cs, arr, sizeof arr);
memcpy(arr, &cs, sizeof arr);

这对于所有现实世界的系统来说都是“可移植的”。

另一个选项是通过添加一个union为结构提供两个不同的变量表示,如下所示:

代码语言:javascript
复制
typedef union {
  struct // C11 anonymous struct
  {
    double re;
    double im;
  };
  double arr[2];
}ComplexStruct;

内部结构可能仍然有填充,所以您仍然应该添加一个正式的静态断言。但这允许您灵活地将数据内容作为单独的成员或数组使用。

最后,C语言实际上支持复数。double _Complex是标准C,而complex.h是一个标准化的复杂库。请参阅How to work with complex numbers in C?

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57787885

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档