假设我具有结构:
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
循环快吗?
答案 0 :(得分:3)
编译器中的优化器应该可以很好地处理该代码,您无需进行太多更改即可使其达到最佳状态。但是,如果要将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,则每次迭代会将整个东西展开为一个负载和一个存储。
答案 1 :(得分:2)
是的,您可以使用memcpy,但要注意以下几点:
如果在上述操作之后仍要执行此操作,则应使用以下代码:
/* NOTE: sizeof(ComplexStructVec) === sizeof(ComplexArrVec) */
memcpy((void *) ComplexStructVec,
(void *) ComplexArrVec,
sizeof(ComplexStructVec)*NumEl);
这是因为在两种情况下都使用向量(数组),因此仅使用它们的名称即可得到它们的地址。 memcpy
将目标地址和源地址定义为void *
,因此我强制转换参数。要复制的字节数是结构或数组的大小(以字节为单位)(请参见“注释”)乘以向量中的条目数。可能不需要(void *)
强制转换。它取决于编译器,语言标准级别和其他编译时间限定符。
还请注意,我故意没有返回值的位置,它是指向目标的指针。如果需要此信息,请当心,因为将其保存到ComplexStructVec
可能会导致编译器(或更糟糕的情况是运行时)问题,具体取决于(在编译器中或在运行时)如何分配它。< / p>
更完整的示例:
void copy(ComplexStruct* ComplexStructVec, ComplexArr* ComplexArrVec)
{
unsigned NumEl = 1000;
memcpy(ComplexStructVec, ComplexArrVec, sizeof(ComplexStruct)*NumEl);
}
答案 2 :(得分:1)
最可移植的方法是循环,如您的示例所示。有时称为结构的序列化/反序列化。
结构的问题是不能保证它们像数组一样具有一致的内存布局。为了避免对齐问题,编译器可以随意在任何地方添加填充字节。如果结构仅由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?