C中的内存分配

时间:2010-01-24 22:20:12

标签: c windows-vista malloc buffer-overflow

我对内存分配顺序有疑问。 在下面的代码中,我在循环中分配4个字符串。 但是当我打印地址时,它们似乎并没有一个接一个地分配......我做错了什么,或者是OS实施的某种防御机制,以防止可能的缓冲区溢出? (我使用的是Windows Vista)。

谢谢。

 char **stringArr;
 int size=4, i;

 stringArr=(char**)malloc(size*sizeof(char*));
 for (i=0; i<size; i++)
    stringArr[i]=(char*)malloc(10*sizeof(char));

 strcpy(stringArr[0], "abcdefgh");
 strcpy(stringArr[1], "good-luck");
 strcpy(stringArr[2], "mully");
 strcpy(stringArr[3], "stam");

 for (i=0; i<size; i++) {
  printf("%s\n", stringArr[i]);
  printf("%d  %u\n\n", &(stringArr[i]), stringArr[i]);
 }

输出:

  

ABCDEFGH   9650064 9650128

     

好运   9650068 9638624

     

mully   9650072 9638680

     

斯塔姆   9650076 9638736

7 个答案:

答案 0 :(得分:7)

通常,当您通过malloc()请求内存时,C运行时库会将请求的大小舍入到某个最小分配大小。这确保了:

  • 运行时库具有记账信息的空间
  • 运行时库管理分配的块的效率更高,这些块都是某个大小的倍数(例如16个字节)

但是,这些是实现细节,您无法真正依赖malloc()的任何特定行为。

答案 1 :(得分:4)

  

但是当我打印地址时,他们似乎并没有一个接一个地分配......

因此?

  

我做错了什么,或者是OS实施的某种防御机制,以防止可能的缓冲区溢出?

可能“不”。

出于兴趣,你得到了什么地址?

答案 2 :(得分:4)

您不应该依赖malloc返回的任何特定排序或值间隔。它以神秘和不可预测的方式表现。

答案 3 :(得分:4)

通常可以合理地预期一系列按时间顺序的分配将导致某种程度上相关的内存地址,但正如其他人所指出的那样,它肯定不是堆管理器的要求。但是,在这种特殊情况下,您可能会看到low fragmentation heap的结果。 Windows保留了可以快速满足请求的小块内存列表。这些可以是任何顺序。

答案 4 :(得分:3)

您不能依赖malloc来为您提供连续的地址。这完全取决于实现,可能是堆的当前状态;一些实现可能,许多不会。

如果您需要地址是连续的,请分配一个大的内存块并设置指针以指向其中的不同区域。

答案 5 :(得分:2)

正如其他人所提到的,没有标准来指定malloc()分配的内存块应该以哪种顺序位于内存中。例如,释放的块可以分散在堆中,并且可以按任何顺序重复使用。

但即使这些块碰巧是一个接一个,它们很可能不会形成一个连续的块。为了减少碎片,堆管理器仅分配特定大小的块,例如2的幂(64,128,256,512等字节)。因此,如果为字符串保留10个字节,那么可能会有22或54个未使用的字节。

内存开销是除非真的有必要使用动态内存分配不是一个好主意的另一个原因。使用静态数组更容易,更安全。

答案 6 :(得分:1)

由于您对了解malloc()返回的地址感兴趣,因此应确保正确打印它们。通过“正确”,我的意思是您应该使用printf()的正确格式说明符来打印地址。你为什么一个人使用"%u"而另一个人使用"%d"

您应该使用"%p"来打印指针。这也是您需要在C中进行强制转换的罕见情况之一:因为printf()是一个可变参数函数,编译器无法告诉您作为参数传递给它的指针需要是是否输入void *

此外,您不应该转换malloc()的返回值。

修正了上述内容后,该程序为:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char **stringArr;
    int size=4, i;

    stringArr = malloc(size * sizeof *stringArr);

    for (i=0; i < size; i++)
        stringArr[i] = malloc(10 * sizeof *stringArr[i]);

    strcpy(stringArr[0], "abcdefgh");
    strcpy(stringArr[1], "good-luck");
    strcpy(stringArr[2], "mully");
    strcpy(stringArr[3], "stam");

    for (i=0; i<size; i++) {
        printf("%s\n", stringArr[i]);
        printf("%p %p\n", (void *)(&stringArr[i]), (void *)(stringArr[i]));
    }
    return 0;
}

我运行时得到以下输出:

abcdefgh
0x100100080 0x1001000a0
good-luck
0x100100088 0x1001000b0
mully
0x100100090 0x1001000c0
stam
0x100100098 0x1001000d0

在我的计算机上,char **指针的长度为8个字节,因此&stringArr[i+1]&stringArr[i]大8个字节。标准保证这一点:如果你malloc()有一些空间,那个空间是连续的。你为4个指针分配了空间,这四个指针的地址彼此相邻。您可以通过以下方式更清楚地看到:

printf("%d\n", (int)(&stringArr[1] - &stringArr[0]));

这应打印1。

关于随后的malloc(),因为每个stringArr[i]都是从单独的malloc()获得的,所以实现可以自由地为它们分配任何合适的地址。在我的实现中,使用该特定运行,地址都是0x10字节。

对于您的实现,似乎char **指针的长度为4个字节。

关于您的个别字符串的地址,看起来malloc()正在进行某种随机化(允许这样做)。