首页 文章

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

提问于
浏览
3

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

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

它显然只有2个字符和1个int(实际上指向int的指针,但无论如何) . 但是你可以用这样的方式调用malloc来使struct保持2个字符和你想要的多个int(让我们说10):

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

此处的输出将在屏幕上打印“50”,这意味着结构内部的数组最多可保持10个整数 .

我对经验丰富的C程序员的问题:

  • 幕后发生了什么?计算机是否为标准的“struct test”分配2 4(2个字符串指向int)字节,然后是4 * 9个字节的内存,让指针“ptr”在这些额外的字节上放置它想要的任何数据类型?

  • 这个技巧只在结构中有数组时才有效吗?

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

3 回答

  • 6

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

    已经不正确了 . 数组不是指针 . 您的struct为2 char 和1 int 保留空间 . 那里没有任何指针 . 你声明的内容基本上等同于

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

    1个元素的数组和普通变量之间没有太大区别(仅存在概念差异,即语法糖) .

    ...但是你可以用这样的方式调用malloc来使它保持1个字符和你想要的多个整数(让我们说10个)...

    呃...如果你想要它持有1 char ,为什么你用2 char s声明你的结构?

    无论如何,为了实现灵活大小的数组作为结构的成员,您必须将数组放在结构的最末端 .

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

    幕后发生了什么?计算机是否为标准的“struct test”分配2 4(2个字符串指向int)字节,然后是4 * 9个字节的内存,让指针“ptr”在这些额外的字节上放置它想要的任何数据类型?

    malloc 分配您要求分配的内存量 . 它只是原始内存的单个平坦块 . 没有其他事情发生"behind the scenes" . 你的结构中没有任何类型的"pointer to int",所以任何涉及"pointer to int"的问题都没有任何意义 .

    这个技巧只在结构中有数组时才有效吗?

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

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

    它没有管理任何东西 . 如果数组不是结构的最后一个成员,那么尝试使用数组的额外元素将会废弃在数组之后声明的结构的成员 . 这很没用,这就是“灵活”阵列必须是最后一个成员的原因 .

  • 0

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

    让我们看一个不涉及动态分配的示例:

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

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

    C语言不需要数组边界检查,但它使得访问数组的行为在其声明的边界之外是未定义的 . 任何事情都可能发生 - 包括使代码安静地按照您希望的方式运行 . 实际上,C允许数组边界检查;它只是没有实现它 .

    对于这样的示例,您最有可能在存在优化时遇到可见问题 . 允许编译器(特别是优化编译器)假定您的程序的行为是明确定义的,并重新排列生成代码以利用该假设 . 如果你写

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

    允许编译器假定索引操作不超出数组的声明边界 . 正如亨利斯宾塞写的那样,"If you lie to the compiler, it will get its revenge" .

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

  • -1

    不,那不行 . 您不能通过在运行时使用malloc()来更改结构的不可变大小(毕竟是编译时分配) . 但是你可以分配一个内存块,或者改变它的大小,这样它就可以容纳多个结构:

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

    这就是在这种情况下你可以用malloc()做的所有事情 .

相关问题