Reputation: 13
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.
Upvotes: 1
Views: 332
Reputation: 31409
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
Upvotes: 1
Reputation: 224427
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.
Upvotes: 1
Reputation: 214475
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;
Upvotes: 3
Reputation: 173
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
.
Upvotes: 0
Reputation: 181104
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.
Upvotes: 1