通过Malloc增加分配给结构的内存大小

我刚刚了解到,使用malloc函数时可以增加分配给结构的内存大小。 例如,你可以有这样的结构:

struct test{
    char a;
    int v[1];
    char b;
};

其中显然只有2个字符和1个int的空间(实际上指向int,但无论如何)。 但是你可以通过这种方式调用malloc来使结构体拥有2个字符和任意数量的整数(比如10):

int main(){
    struct test *ptr;
    ptr = malloc (sizeof(struct test)+sizeof(int)*9);
    ptr->v[9]=50;
    printf("%dn",ptr->v[9]);   
return 0;
}

这里的输出将在屏幕上显示为“50”,这意味着结构内的数组最多可以保持10个整数。

我对那些有经验的C程序员的问题:

  • 这里幕后发生了什么? 计算机是否为标准“结构测试”分配了2 + 4(2个字符+指向int的字节)字节,然后再增加4 * 9个字节的内存,并让指针“ptr”将这些额外的数据字节?

  • 这个技巧只有在struct中有一个数组时才有效?

  • 如果数组不是该结构的最后一个成员,那么计算机如何管理分配的内存块?


  • ...显然只有2个字符和1个int的空间(实际上指向int,但无论如何)...

    已经不正确。 数组不是指针。 你的结构体拥有2个char和1个int 。 那里没有任何指针。 你所宣称的基本上相同

    struct test {
        char a;
        int v;
        char b;
    };
    

    1元素数组和普通变量之间没有太大区别(只有概念差异,即语法糖)。

    ...但是你可以用这种方法调用malloc,使其拥有1个字符和任意数量的整数(比如说10)...

    呃...如果你想要它保存1个char ,为什么你用2个char声明你的结构?

    无论如何,为了实现一个灵活大小的数组作为结构的一员,你必须将你的数组放在结构的最后。

    struct test {
        char a;
        char b;
        int v[1];
    };
    

    然后,您可以为结构分配内存,并在结尾处为阵列分配一些“额外”内存

    struct test *ptr = malloc(offsetof(struct test, v) + sizeof(int) * 10);
    

    (注意offsetof用于计算正确的大小)。

    这样它会工作,给你一个在结构中的大小10和2个char数组(声明)。 它被称为“struct hack”,它严格依赖于该数组是结构的最后一个成员。

    C99版本的C语言专门支持“struct hack”。 在C99中,它可以完成

    struct test {
        char a;
        char b;
        int v[];
    };
    
    ...
    struct test *ptr = malloc(sizeof(struct test) + sizeof(int) * 10);
    

    这里幕后发生了什么? 计算机是否为标准“结构测试”分配了2 + 4(2个字符+指向int的字节)字节,然后再增加4 * 9个字节的内存,并让指针“ptr”将这些额外的数据字节?

    malloc分配尽可能多的内存,你要求它分配。 它只是一个单一的原始记忆块。 “幕后”没有其他事情发生。 在你的结构中没有任何类型的“int指针”,因此任何涉及“指向int的指针”的问题根本没有意义。

    这个技巧只有在struct中有一个数组时才有效?

    那么,这就是重点:访问额外的内存,就好像它属于一个声明为结构最后一个成员的数组。

    如果数组不是该结构的最后一个成员,那么计算机如何管理分配的内存块?

    它不管理任何事情。 如果数组不是该结构的最后一个成员,那么尝试使用该数组的额外元素将会清除在数组之后声明的结构成员。 这非常没用,这就是为什么“灵活”数组必须是最后一个成员。


    不,那不行。 通过在运行时使用malloc(),不能更改结构的不可变大小(毕竟是编译时分配)。 但是你可以分配一个内存块,或者改变它的大小,使它拥有多个结构体:

    int main(){
        struct test *ptr;
        ptr = malloc (sizeof(struct test) * 9);
    }
    

    这就是关于在这种情况下使用malloc()所能做的所有事情。


    除了其他人告诉你的内容(总结:数组不是指针,指针不是数组,读取comp.lang.c FAQ的第6节),尝试访问最后一个元素之后的数组元素会调用未定义的行为。

    我们来看一个不涉及动态分配的例子:

    struct foo {
        int arr1[1];
        int arr2[1000];
    };
    
    struct foo obj;
    

    该语言保证obj.arr1将从偏移量0开始分配,并且obj.arr2的偏移量将为sizeof (int)或更多(编译器可以在结构成员之间和最后一个成员之后插入填充,但不会在第一)。 所以我们知道在obj.arr1之后的obj中有足够的空间来存放多个int对象。 这意味着,如果你写obj.arr1[5] = 42 ,然后访问obj.arr[5] ,你可能会得到你存储在那里的值42 (你可能会obj.arr2[4] )。

    C语言不需要进行数组边界检查,但它会使未定义的数组访问超出其声明边界的行为。 任何事情都可能发生 - 包括让代码安静地按照你想要的方式行事。 事实上,C允许数组边界检查; 它只是没有提供处理错误的方法,大多数编译器都没有实现它。

    对于这样的例子,你最有可能遇到优化存在的可见问题。 一个编译器(特别是一个优化编译器)可以假定你的程序的行为是精确定义的,并且重新排列生成的代码以利用这个假设。 如果你写

    int index = 5;
    obj.arr1[index] = 42;
    

    允许编译器假定索引操作不会超出数组的声明边界。 正如亨利斯宾塞写道,“如果你对编译器说谎,它会得到它的报复”。

    严格地说,struct hack可能涉及未定义的行为(这就是为什么C99添加了一个明确定义的版本),但它被广泛使用,大多数或所有编译器都会支持它。 这在comp.lang.c FAQ的问题2.6中有介绍。

    链接地址: http://www.djcxy.com/p/80207.html

    上一篇: Increasing The Size of Memory Allocated to a Struct via Malloc

    下一篇: treating allocated memory as a different type