假设我有结构:
typedef struct {
double re;
double im;
}ComplexStruct;和数组:
typedef double ComplexArr[2];// [0] -> real, [1] -> imag
今天,我将从ComplexStruct复制到ComplexArr,反之亦然,使用简单的for循环:
//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循环更快的方法吗?
发布于 2019-09-04 12:11:33
编译器中的优化器应该能够很好地完成这些代码,您不需要对其进行太多的更改就可以使其优化。但是,如果要将ComplexStructVec和ComplexArrVec传递到函数中,则应该将它们标记为restrict,以便编译器知道不存在别名。如下所示:
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,它将在每次迭代时将整个事件展开为一个加载和一个存储。
发布于 2019-09-04 12:52:26
是的,您可以使用memcpy,但有几个注意事项:
如果在完成上述操作之后,您仍然希望这样做,那么下面的代码应该可以做到这一点:
/* NOTE: sizeof(ComplexStructVec) === sizeof(ComplexArrVec) */
memcpy((void *) ComplexStructVec,
(void *) ComplexArrVec,
sizeof(ComplexStructVec)*NumEl);这是因为在这两种情况下都使用向量(数组),所以只使用它们的名称就可以得到它们的地址。memcpy将目标地址和源地址定义为void *,因此我转换了参数。要复制的字节数是以字节为单位的结构或数组的大小(参见注),乘以向量中的条目数。可能不需要(void *)强制转换。它取决于编译器、语言标准级别和其他编译时间限定符。
还要注意,我故意没有返回值的位置,返回值是指向目标的指针。如果您需要此信息,请小心,因为将其保存到ComplexStructVec可能会导致编译器(或更糟的是运行时)问题,这取决于编译器或运行时如何分配它。
一个更完整的例子:
void copy(ComplexStruct* ComplexStructVec, ComplexArr* ComplexArrVec)
{
unsigned NumEl = 1000;
memcpy(ComplexStructVec, ComplexArrVec, sizeof(ComplexStruct)*NumEl);
}发布于 2019-09-04 14:39:14
最可移植的方法是循环,如您的示例所示。这有时被称为结构的序列化/反序列化。
结构的问题是,它们不能保证具有一致的内存布局,就像数组一样。为了避免对齐问题,编译器可以任意添加填充字节。对于仅由8字节double组成的结构,填充是非常不可能的。但从形式上讲,它并不是便携的。
但是,您可以相当安全地执行以下操作:
_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为结构提供两个不同的变量表示,如下所示:
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?
https://stackoverflow.com/questions/57787885
复制相似问题