简体   繁体   English

在C中使用memcpy将数组复制到struct,反之亦然

[英]Using memcpy for copying array to struct and vice versa in C

Suppose I have structure: 假设我有结构:

typedef struct {
double re;
double im;
}ComplexStruct;

and array: 和数组:

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

today I am copying from ComplexStruct to ComplexArr and vice versa using simple for loop: 今天我使用简单的for循环从ComplexStruct复制到ComplexArr ,反之亦然:

//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;
}

Is there a way to this safely using memcpy for at least one of the directions? 有没有办法安全地使用memcpy至少一个方向? Is there another way which will by faster than for loop? 有这将快于另一种方式for循环?

The optimizer in your compiler should do a great job with that code, you don't need to change much to make it optimal. 编译器中的优化器应该可以很好地处理该代码,您无需进行太多更改即可使其最佳化。 However, if you are passing ComplexStructVec and ComplexArrVec into a function, you should mark them as restrict so the compiler knows there is no aliasing going on. 但是,如果要将ComplexStructVec和ComplexArrVec传递给函数,则应将它们标记为restrict以便编译器知道不存在别名。 Like this: 像这样:

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];
    }
}

By doing that you eliminate a whole bunch of generated code because it doesn't need to handle the possibility that the two arguments overlap. 通过这样做,您可以消除一大堆生成的代码,因为它不需要处理两个参数重叠的可能性。

Demo: https://godbolt.org/z/F3DUaq (just delete "restrict" there to see the difference). 演示: https//godbolt.org/z/F3DUaq (只需在那里删除“限制”以查看区别)。 If NumEl is less than 18 it will unroll the whole thing into one load and one store per iteration. 如果NumEl小于18,它将整个事物展开为一次加载,每次迭代一次存储。

Yes, you can use memcpy, with a few caveats: 是的,你可以使用memcpy,但需要注意几点:

  1. The layout of the array and structure are identical, meaning that the compiler does not align either the items in the array or entries in the structure. 数组和结构的布局是相同的,这意味着编译器不会对齐数组中的项或结构中的条目。
  2. The memory associated with the struct and array are identical in size. 与struct和array关联的内存大小相同。
  3. You are not concerned with portability to other architectures (which may change the answers to #1 and/or #2). 您不关心其他体系结构的可移植性(可能会改变#1和/或#2的答案)。
  4. This is not an ideal programming technique as it has some potential pitfalls, as noted above). 这不是一种理想的编程技术,因为它有一些潜在的缺陷,如上所述)。

If after the above you still want to do this, the following code should do the trick: 如果在上面你仍然想要这样做,下面的代码应该做的伎俩:

/* NOTE: sizeof(ComplexStructVec) === sizeof(ComplexArrVec) */
memcpy((void *) ComplexStructVec,
       (void *) ComplexArrVec,
       sizeof(ComplexStructVec)*NumEl);

What this does is, since you are using vectors (arrays) in both cases, you have the address of them by using just their names. 这样做是因为你在两种情况下使用向量(数组),只需使用它们的名称就可以得到它们的地址。 memcpy defines the destination and source addresses as void * , so I cast the arguments. memcpy将目标和源地址定义为void * ,因此我转换了参数。 The number of bytes to copy is the size, in bytes, of the structure or array (see NOTE) times the number of entries in the vector. 要复制的字节数是结构或数组的大小(以字节为单位)(请参阅注释)乘以向量中的条目数。 The (void *) cast may not be required. 可能不需要(void *)演员表。 It depends upon the compiler, the language standard level, and other compile time qualifiers. 它取决于编译器,语言标准级别和其他编译时限定符。

Also note, I intentionally did not have a place for the return value, which is a pointer to the destination. 另请注意,我故意没有返回值的位置,这是指向目标的指针。 If you want this information, be careful, as saving it to ComplexStructVec may cause either compiler (or worse case run-time) issues, depending on how it was allocated (by the compiler or during run-time). 如果您需要此信息,请注意,因为将其保存到ComplexStructVec可能会导致编译器(或更糟糕的情况下运行时)问题,具体取决于它是如何分配的(由编译器或在运行时)。

A more complete example: 一个更完整的例子:

void copy(ComplexStruct* ComplexStructVec, ComplexArr* ComplexArrVec)
{
    unsigned NumEl = 1000;
    memcpy(ComplexStructVec, ComplexArrVec, sizeof(ComplexStruct)*NumEl);
}

The most portable way is the loops as in your example. 最便携的方式是示例中的循环。 This is sometimes referred to as serializing/de-serializing of the struct. 这有时被称为结构的序列化/反序列化。

The problem with structs is that they aren't guaranteed to have a consistent memory layout, like arrays are. 结构的问题在于它们不能保证具有一致的内存布局,就像数组一样。 To dodge alignment problems, the compiler is free to add padding bytes anywhere. 为避免对齐问题,编译器可以随意在任何地方添加填充字节。 In case of a struct consisting of nothing but 8 byte double , padding is highly unlikely. 如果一个结构只包含8个字节的double精度,那么填充是不太可能的。 But still, formally it isn't portable. 但是,正式地说它不是便携式的。

You can however fairly safely do the following: 但是,您可以相当安全地执行以下操作:

_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);

This is "reasonably portable" to all real-world systems. 这对所有真实世界的系统来说都是“合理的便携”。

Another option is to give the struct two different variable representations by adding a union , like this: 另一种选择是通过添加union来为结构提供两个不同的变量表示,如下所示:

typedef union {
  struct // C11 anonymous struct
  {
    double re;
    double im;
  };
  double arr[2];
}ComplexStruct;

The inner struct may still have padding, so you should add a formal static assert still. 内部结构可能仍然有填充,所以你应该添加一个正式的静态断言。 But this allows you the flexibility to use the data contents either as individual members or as an array. 但是,这使您可以灵活地将数据内容用作单个成员或数组。

And finally, C actually has language support for complex numbers. 最后,C实际上对复杂数字有语言支持。 double _Complex is standard C, and complex.h is a standardized complex library. double _Complex是标准C, complex.h是标准化的复杂库。 See How to work with complex numbers in C? 请参阅如何在C中使用复数?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM