简体   繁体   中英

How do i copy members of a struct into an array of double in C?

I have a struct of a struct having members like this

typedef struct{
    double xMin;
    double yMin;
    double zMin;
    double xMax;
    double yMax;
    double zMax;
    double xMedian;
    double yMedian;
    double zMedian;
    double xMean;
    double yMean;
    double zMean;
    double xVar;
    double yVar;
    double zVar;
    double xStD;
    double yStD;
    double zStD;
}featureSet_t;

typedef struct{
    featureSet_t acc_features;
    featureSet_t gyr_features;
    featureSet_t mag_features;
}combinedFeatureSet_t; 

combinedFeatureSet_t featureSetOfPolledData;
double copy[54] = {0};

now i want to copy the members of the above instance into the array of double say, for eg.,

copy[0] =  featureSetOfPolledData.acc_features.xMin;
..
..
copy[53] =  featureSetOfPolledData.mag_features.zstD;

Its too tedious. Pls help whats the best way to do this.

The best solution is to simply use memcpy. You'll need to guard against padding in the struct - there should be none in this case, but it is good practice to always do a compile-time check:

_Static_assert(sizeof(featureSetOfPolledData) == sizeof(double[54]), 
               "Unexpected padding detected.");

Once that's checked, then just merrily memcpy away:

memcpy(copy, &featureSetOfPolledData, sizeof(double[54]));

If you for some reason need to index individual item names, and changing the types is an option, consider using unions:

typedef union
{
  struct
  {
    double xMin;
    double yMin;
    double zMin;
    double xMax;
    double yMax;
    double zMax;
    double xMedian;
    double yMedian;
    double zMedian;
    double xMean;
    double yMean;
    double zMean;
    double xVar;
    double yVar;
    double zVar;
    double xStD;
    double yStD;
    double zStD;
  };
  double array [18];
}featureSet_t;

typedef union
{
  struct
  {
    featureSet_t acc_features;
    featureSet_t gyr_features;
    featureSet_t mag_features;
  };
  double array [18*3];  
}combinedFeatureSet_t; 

Now it would be possible to do stuff like:

for(size_t i=0; i<54; i++)
{
  copy[i] = featureSetOfPolledData.array[i];
}

but also to access individual items by name:

featureSetOfPolledData.acc_features.xMin = 1.0;

Lundin provided a good answer

This answer is not a solution. It's more of a warning of something common that you should not do. It can be tempting do do something like this, but you shouldn't.

double *ptr = &featureSetOfPolledData;
for(int i=0; i<53; i++) copy[i] = ptr[i];

This is likely to work, but does invoke undefined behavior.

So why does this cause undefined behavior? It's not completely obvious. The members in the struct is guaranteed to be in the same order as you wrote them. Padding can occur in a struct, but for doubles it is extremely unlikely that any compiler would use padding.

To be honest, I don't remember the details, but it has to do with that accessing the fields via a pointer this way just simply is undefined behavior in the C standard, and ANY undefined behavior may break your code. Especially if you are enabling optimization.

Lundin (same great guy again) wrote this in the comments below:

It is UB because the struct layout isn't guaranteed to hold the same alignment as the double (it will in this case, in practice), but also because featureSetOfPolledData is not an array. The part of the standard is the additive operators C17 6.5.6 "For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type." And then you go beyond your 1 element large array. The compiler is allowed to assume that you didn't access the data.

C17 6.3.2.3 gives a special exception that allows you to dissect larger types by using character types. So just for the record, you can actually do like this well-defined by using a character type, increase it with sizeof(double) at each iteration, then convert to double. Like

for(uint8_t* ptr = &featureSetOfPolledData; ptr < (uint8_t*)&featureSetOfPolledData + sizeof(double[54]); ptr += sizeof(double)) { double d = *(double*)ptr; /* Put the copying code here */ }

Complete madness, but well-defined

Assigning structure members to your array individually, as you describe, is the only strictly conforming way to do the job. Any other alternative requires making assumptions about the layout of the struct members, and C does not make strong enough guarantees about that to support what you want.

However, if you are willing to assume that featureSet_t is laid out without any padding between members, or if you can use an implementation extension to ensure that, and if you want your array to be populated in structure member order, one structure's members after the other's, then you can use memcpy() to do this:

double copy[54];

memcpy(copy,      &featureSetOfPolledData.acc_features, 18 * sizeof(double));
memcpy(copy + 18, &featureSetOfPolledData.gyr_features, 18 * sizeof(double));
memcpy(copy + 36, &featureSetOfPolledData.mag_features, 18 * sizeof(double));

That achieves pretty good code simplicity in exchange for non-trivial assumptions about structure layout that nevertheless probably hold in practice. You might, conceivably, get away with just one memcpy() of the overall featureSetOfPolledData object, but IMO, the minimal additional code simplification achieved that way does not justify the stronger assumptions about layout.

The closest thing that might work as far as sticking to the standard would be to create a union of the struct and the array and to validate that they are the same size:

static_assert(sizeof(combinedFeatureSet_t) == sizeof(double [54]), "size mismatch");

typedef union {
    combinedFeatureSet_t featureset_struct;
    double featureset_list[54];
} featureset_u;

featureset_u u;
u.featureset_struct = featureSetOfPolledData;
// read u.featureset_list

The static_assert verifies that the struct has no internal padding. With that, the array members overlay exactly the struct members and they have the same type.

With this union, you can freely read from the array and write to the struct or vice versa.

You can probably create a function taking a combinedFeatureSet_t and returning a double array . It calls another function returning a double array from your featureSet_t for each featureSet_t . From there, you'll only have to call the first function to get your array .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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