ayfkly
ayfkly

Reputation: 301

How to initialize struct with an array?

Is there a better way of initializing a struct with an array than doing the following?

struct Parameters
{
    double distance;
    double radius;
    double strength;
    long distanceX;
    long distanceY;
    long clickX;
    long clickY;
};

void calculate(double dParameters[], long lParameters[])
{
    Parameters param = 
    {
        dParameters[0], 
        dParameters[1],
        dParameters[2],

        lParameters[0],
        lParameters[1],
        lParameters[2],
        lParameters[3]
    };
}

I thought of assigning pointers:

void calculate(double dParameters[], long lParameters[])
{
    Parameters param;
    (double*)(&param.distance) = &dParameters[0];
    (long*)(&param.distanceX) = &lParameters[0];
}

But I am not sure if it is valid in c++.

Upvotes: 0

Views: 83

Answers (2)

TrentP
TrentP

Reputation: 4722

If you know the layout of the struct, and have carefully chosen to put all members of like type in order without anything between them, then you could use memcpy().

memcpy(&param.distance, dParameters, sizeof(*dParameters) * 3);
memcpy(&param.distanceX, lParameters, sizeof(*lParameters) * 4);

This is rather fragile code, as distance must be the first double parameter of exactly four double parameters in a row, or you'll get corrupted data, and nothing will verify this at compile time.

It could be improved with offsetof to get and/or verify the length. Such as:

void calculate(double dParameters[], size_t n_dParameters, long lParameters[], size_t n_lParameters)
{
    Parameters param;

    assert(offsetof(Parameters, strength) - offsetof(Parameters, distance) == sizeof(*dParameters) * n_dParameters);
    memcpy(&param.distance, dParameters, offsetof(Parameters, strength) - offsetof(Parameters, distance));
    assert(offsetof(Parameters, clickY) - offsetof(Parameters, distanceX) == sizeof(*lParameters) * n_lParameters);
    memcpy(&param.distanceX, dParameters, offsetof(Parameters, clickY) - offsetof(Parameters, distanceX));
}

Historically, gcc has not been great at optimizing struct initialization, such as using the equivalent of memcpy() or memset() when it would be possible and beneficial. If your struct had a hundred fields, this might actually be useful.

Another technique would be use to a union to define both an array version and an individual field version of your struct.

struct ParametersArrays {
   double doubles[3];
   long longs[4];
};
union ParametersUnion {
   struct Parameters params;
   struct ParametersArrays arrays;
};
ParametersUnion u;
memcpy(u.arrays.doubles, dParameters, sizeof(u.arrays.doubles));
memcpy(u.arrays.longs, lParameters, sizeof(u.arrays.longs));
Parameters& p = u.params; // Now you can use p

Note that using more than one member of a union like this is not strictly legal in C++, but it is in C, and most/all C++ compilers will compile it as expected.

Upvotes: 1

MSalters
MSalters

Reputation: 180145

Your second example is illegal, but chances are that the optimizer (knowing the actual layout) does implement it like that.

Upvotes: 0

Related Questions