Как я могу скопировать членов структуры в массиве double в C? - PullRequest
1 голос
/ 03 мая 2019

У меня есть структура структуры, в которой есть такие члены

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

Теперь я хочу скопировать элементы вышеупомянутого экземпляра в массив двойных слов, например,

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

Это слишком утомительно.Пожалуйста, помогите, что лучший способ сделать это.

Ответы [ 5 ]

3 голосов
/ 03 мая 2019

Лучшее решение - просто использовать memcpy. Вам нужно будет защититься от заполнения в структуре - в этом случае их не должно быть, но рекомендуется всегда проверять время компиляции:

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

После того, как это проверено, просто весело вспомните:

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

Если вам по какой-то причине необходимо индексировать отдельные имена элементов, и изменение типов является вариантом, рассмотрите возможность использования союзов:

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; 

Теперь можно было бы делать что-то вроде:

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

, но также для доступа к отдельным элементам по имени:

featureSetOfPolledData.acc_features.xMin = 1.0;
1 голос
/ 03 мая 2019

Индивидуальное присвоение элементов структуры вашему массиву, как вы описываете, является единственным строго соответствующим способом выполнить работу.Любая другая альтернатива требует предположений о расположении членов структуры, и C не дает достаточно сильных гарантий относительно этого для поддержки того, что вы хотите.

Однако, если вы хотите предположить, что featureSet_t заложенобез каких-либо дополнений между членами, или если вы можете использовать расширение реализации, чтобы гарантировать, что, и если вы хотите, чтобы ваш массив заполнялся в порядке элементов структуры, членов одной структуры после другого, то вы можете использовать memcpy() для этого:

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

Это обеспечивает довольно хорошую простоту кода в обмен на нетривиальные предположения о структуре структуры, которые, тем не менее, , вероятно, имеют место на практике.Можно предположить, что вам может сойти с рук только один memcpy() от общего объекта featureSetOfPolledData, но IMO, минимальное дополнительное упрощение кода, достигнутое таким образом, не оправдывает более строгие предположения о компоновке.

1 голос
/ 03 мая 2019

Самое близкое, что может сработать, если придерживаться стандарта, это создать union структуры и массива и проверить, что они имеют одинаковый размер:

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

static_assert подтверждает, что структура не имеет внутреннего заполнения.При этом члены массива перекрывают в точности элементы структуры и имеют одинаковый тип.

С помощью этого объединения вы можете свободно читать из массива и записывать в структуру или наоборот.

1 голос
/ 03 мая 2019

Лундин предоставил хороший ответ

Этот ответ не является решением.Это скорее предупреждение о чем-то обычном, что вы не должны делать.Может быть заманчиво сделать что-то подобное, но вы не должны.

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

Это может сработать, но вызывает неопределенное поведение.

Так почему это вызывает неопределенное поведение?Это не совсем очевидно.Члены в структуре гарантированно будут в том же порядке, в котором вы их написали.Заполнение может происходить в структуре, но для двойников крайне маловероятно, что любой компилятор будет использовать заполнение.

Если честно, я не помню деталей, но это связано с тем, что доступ к полям осуществляется черезуказатель таким образом просто является неопределенным поведением в стандарте C, и ЛЮБОЕ неопределенное поведение может нарушить ваш код.Особенно, если вы включаете оптимизацию.

Лундин (снова тот же замечательный парень) написал это в комментариях ниже:

Это UB, потому что структура структуры не может содержатьтакое же выравнивание, как у double (так будет и в этом случае на практике), но также потому, что featureSetOfPolledData не является массивом.Часть стандарта - аддитивные операторы. C17 6.5.6. «Для целей этих операторов указатель на объект, который не является элементом массива, ведет себя так же, как указатель на первый элемент массива длиной один.с типом объекта в качестве его типа элемента. "И тогда вы выходите за пределы вашего массива из 1 элемента.Компилятору разрешается предполагать, что вы не обращались к данным.

C17 6.3.2.3 дает специальное исключение, которое позволяет разбирать большие типы с помощью символьных типов.Так что просто для записи, вы действительно можете сделать это хорошо определенным, используя символьный тип, увеличивая его на sizeof (double) на каждой итерации, а затем преобразовывая в double.Как

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

Полное безумие, но четко определенное

0 голосов
/ 03 мая 2019

Вы, вероятно, можете создать функцию, принимающую combinedFeatureSet_t и возвращающую double array.Он вызывает другую функцию, возвращающую double array из вашего featureSet_t для каждого featureSet_t.Оттуда вам нужно будет только вызвать первую функцию, чтобы получить array.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...