Допустимо ли встраивать массивы переменной длины в структуры, перераспределяя память? - PullRequest
0 голосов
/ 11 февраля 2019

У меня есть структура, которую я хотел бы хранить в смежной памяти, чтобы я мог иметь возможность memcpy всей структуры и т. Д. Однако моя структура содержит массив переменной длины. Теперь эта длина будет фиксированной на время выполнения программы , но во время компиляции она неизвестна. Можно ли обойти это, перераспределив память после структуры, чтобы освободить место длямассив?

Так что, если бы я начал с

struct license_plate{
    char issuing_province_territory_code [2];
    char* number;
}

, мне потребовался бы отдельный malloc для number, поэтому я подумал сделать следующее

struct license_plate_v2 {
    char issuing_province_territory_code [3];
    char number[1];
}

и выделите его таким образом

size_t sizeof_license_plate_v2( int number_length ){
    return sizeof(struct license_plate_v2) + number_length * sizeof(char);
}


struct license_plate_v2* malloc_license_plate_v2( int number_length ){
    return malloc( sizeof_license_plate_v2( number_length ) );
}

, а затем сможете перебирать массив, например

struct license_plate_v2* index_license_plate_v2( struct license_plate_v2 *arr, int index, int plate_num_len ){
    return  arr + index * sizeof_license_plate_v2(plate_num_len);
}

void print_all( struct license_plate_v2* plates, int num_of_plates, int plate_num_len ){
    for( int plate_index = 0; plate_index < num_of_plates; plate_index++ ){
        struct license_plate_v2* plate = index_license_plate_v2( plates, plate_index, plate_num_len );
        printf( "where: %s, plate: %s\n", plate->issuing_province_territory_code, plate->number  );
    }
}

Это допустимый C?Это гарантированно работает или я использую неопределенное поведение?Есть ли проблема с байтовым выравниванием, если массив состоит из структур?Есть ли термин для этого?Это правильный путь для достижения такого эффекта?

Кажется, что работа:

#include <stdlib.h>

int main( int argc, char** argv ) {
    //these values could have from from argv for example
    int num_len = 7;


    struct license_plate_v2 *arr = malloc( 4  * sizeof_license_plate_v2(num_len) );

    struct license_plate_v2 *arr_0 = arr + 0 * sizeof_license_plate_v2(num_len);
    memcpy( arr_0->issuing_province_territory_code, "ON"      , 3           * sizeof(char) );
    memcpy( arr_0->number                         , "BFKK281" , (num_len+1) * sizeof(char) );

    struct license_plate_v2 *arr_1 = arr + 1 * sizeof_license_plate_v2(num_len);
    memcpy( arr_1->issuing_province_territory_code, "ON"      , 3           * sizeof(char) );
    memcpy( arr_1->number                         , "BYTR741" , (num_len+1) * sizeof(char) );

    struct license_plate_v2 *arr_2 = arr + 2 * sizeof_license_plate_v2(num_len);
    memcpy( arr_2->issuing_province_territory_code, "ON"      , 3           * sizeof(char) );
    memcpy( arr_2->number                         , "CAAA224" , (num_len+1) * sizeof(char) );

    struct license_plate_v2 *arr_3 = arr + 3 * sizeof_license_plate_v2(num_len);
    memcpy( arr_3->issuing_province_territory_code, "ON"      , 3           * sizeof(char) );
    memcpy( arr_3->number                         , "CASD431" , (num_len+1) * sizeof(char) );

    print_all( arr, 4, 7 );

    free( arr );   
}

PS - это упрощенный пример, чтобы проиллюстрировать вопрос, проблема реального мира включает в себя что-то вроде миллионов мест с тысячами (запуститьно не компилируйте постоянную времени) точек данных, каждая из которых является структурой, а не char, поэтому некоторые очевидные обходные пути не применяются.

1 Ответ

0 голосов
/ 11 февраля 2019

Структуры с гибким членом массива не могут быть элементами массива.Это продиктовано в разделе 6.7.2.1p3 C стандарта :

Структура или объединение не должны содержать члена с неполным или функциональным типом (следовательно, структура не должнасодержать собственный экземпляр, но может содержать указатель на свой экземпляр), за исключением того, что последний член структуры с более чем одним именованным членом может иметь неполный тип массива;такая структура (и любое объединение, содержащее, возможно, рекурсивно член, являющийся такой структурой) не должно быть членом структуры или элементом массива

Причина этого заключается в том, что массивиндексация выполняется путем указания на область памяти, кратную размеру структуры.Но если структура имеет переменный размер, невозможно узнать, где находится следующий экземпляр структуры в памяти.

В вашем конкретном случае максимальная длина номера автомобильного номера не так велика,поэтому просто используйте фиксированный размер, достаточно большой, чтобы вместить любое значение, которое оно может содержать.

struct license_plate{
    char issuing_province_territory_code[3];
    char number[20];
}

Кроме того, способ, которым вы устанавливаете элемент гибкого массива с массивом размера 1, является старым способом сделать это раньшеони были стандартизированы, и это часто упоминается как «взлом структуры».Современный способ объявления элемента гибкого массива имеет неопределенный размер:

struct license_plate_v2 {
    char issuing_province_territory_code [3];
    char number[];
}

И sizeof(struct license_plate_v2) не включает flexible array member.

...