代码之家  ›  专栏  ›  技术社区  ›  Sled bayer

通过过度分配内存在结构中内联可变长度数组是否有效?

  •  2
  • Sled bayer  · 技术社区  · 5 年前

    我有一个结构,我想把它保存在连续的内存中,这样我就可以 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 回复  |  直到 5 年前
        1
  •  5
  •   dbush    5 年前

    结构与A 灵活的数组成员 不能是数组的元素。这在 C standard :

    结构或联盟不得包含不完整或 函数类型(因此,结构不应包含 但可以包含指向自身实例的指针),除非 结构的最后一个成员具有多个 命名成员可能具有不完整的数组类型;此类结构 (以及可能递归地包含 此类结构)不得是结构或构件的构件。 数组的

    原因是数组索引是通过指向一个内存位置来完成的,该内存位置是结构大小的倍数。但是,如果结构的大小可变,就无法知道结构的下一个实例在内存中的位置。

    在您的特定情况下,车牌号的最大长度不是那么大,所以只需使用一个足够大的固定大小来容纳它可能包含的任何值。

    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 .