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

Sle*_*led 2 c arrays struct var undefined-behavior

我有一个结构,我想保持连续的内存,以便我能够memcpy整个结构等.但是我的结构包含一个可变长度的数组.现在这个长度将在程序执行期间得到修复,但在编译时它是未知的.我可以通过在结构之后分配内存来为阵列腾出空间来解决这个问题吗?

所以,如果我开始

struct license_plate{
    char issuing_province_territory_code [2];
    char* number;
}
Run Code Online (Sandbox Code Playgroud)

我需要一个单独malloc的,number所以我想做以下

struct license_plate_v2 {
    char issuing_province_territory_code [3];
    char number[1];
}
Run Code Online (Sandbox Code Playgroud)

并按此分配

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 ) );
}
Run Code Online (Sandbox Code Playgroud)

然后能够迭代像这样的数组

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  );
    }
}
Run Code Online (Sandbox Code Playgroud)

这是有效的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 );   
}
Run Code Online (Sandbox Code Playgroud)

PS-这是一个简化的例子来说明这个问题,现实世界的问题涉及到数百万个位置,数千个(运行但不是编译时间常数)的数据点,每个数据点都是一个结构而不是a char,所以有些明显的工作不适用.

dbu*_*ush 5

具有灵活数组成员的结构不能是数组的元素.这在C标准的 6.7.2.1p3节中规定:

结构或联合不应包含具有不完整或函数类型的成员(因此,结构不应包含其自身的实例,但可以包含指向其自身实例的指针),除了结构的最后一个成员具有多个一个命名成员可能有不完整的数组类型; 这样的结构(以及可能递归地包含这种结构的成员的任何联合)不应该是结构的成员或数组的元素

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

在您的特定情况下,车牌号码的最大长度不是很大,因此只需使用足够大的固定大小来保存它可能包含的任何值.

struct license_plate{
    char issuing_province_territory_code[3];
    char number[20];
}
Run Code Online (Sandbox Code Playgroud)

此外,您设置具有大小为1的数组的灵活数组成员的方式是在标准化之前执行此操作的旧方法,并且它通常被称为"结构黑客".声明灵活数组成员的现代方式具有未指定的大小:

struct license_plate_v2 {
    char issuing_province_territory_code [3];
    char number[];
}
Run Code Online (Sandbox Code Playgroud)

而且sizeof(struct license_plate_v2)不包括flexible array member.