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

时间:2011-09-15 19:06:45

标签: c struct malloc

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

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

这显然只有2个字符和1个(指向现实中的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程序员的问题:

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

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

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

3 个答案:

答案 0 :(得分:6)

  

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

已经不正确了。数组不是指针。您的结构可容纳2 char和1 int的空间。那里没有任何指针。您声明的内容基本上等同于

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

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

  

...但是你可以用这样的方式调用malloc来使它保持1个char和as   你想要的很多一些(比如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”,它主要取决于数组是结构的最后成员。

C语言版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分配你要求分配的内存。它只是原始内存的单个平坦块。没有其他事情发生在“幕后”。你的结构中没有任何类型的“int指针”,所以任何涉及“指向int”的问题都没有任何意义。

  

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

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

  

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

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

答案 1 :(得分:0)

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

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

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

答案 2 :(得分:-1)

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

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

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

struct foo obj;

语言保证从偏移量0开始分配obj.arr1obj.arr2的偏移量为sizeof (int)或更多(编译器可以在结构成员之间插入填充,之后最后一个成员,但不是在第一个成员之前)。因此,我们知道obj之后有足够的空间来存放int之后的多个obj.arr1个对象。这意味着,如果您撰写obj.arr1[5] = 42,然后再访问obj.arr[5],则可能会获取您存储在那里的值42(并且您' ll可能已被摧毁obj.arr2[4])。

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

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

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

允许编译器假设索引操作超出数组的声明边界。正如亨利斯宾塞所写的那样,“如果你对编译器撒谎,它就会报复”。

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