6

我有一个关于内存分配顺序的问题。在下面的代码中,我在循环中分配了 4 个字符串。但是当我打印地址时,它们似乎没有一个接一个地分配......我做错了什么还是操作系统为防止可能的缓冲区溢出而实施的某种防御机制?(我使用 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

穆利 9650072 9638680

斯塔姆 9650076 9638736

4

7 回答 7

7

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

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

但是,这些是实现细节,您不能真正依赖malloc().

于 2010-01-24T22:25:56.333 回答
4

通常可以合理地预期一系列按时间顺序分配的内存地址会产生某种相关的内存地址,但正如其他人所指出的,这当然不是堆管理器的要求。但是,在这种特殊情况下,您可能会看到低碎片堆的结果。Windows 保留可以快速满足请求的小块内存列表。这些可以按任何顺序排列。

于 2010-01-24T22:53:56.517 回答
4

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

所以?

我做错了什么还是操作系统实施了某种防御机制来防止可能的缓冲区溢出?

应该是“都不是”。

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

于 2010-01-24T22:23:35.010 回答
4

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

于 2010-01-24T22:24:09.487 回答
3

你不能依赖 malloc 给你连续的地址。这完全取决于实现,可能是堆的当前状态;有些实现可能,很多不会。

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

于 2010-01-24T22:56:55.123 回答
2

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

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

内存开销是另一个原因,除非确实必要,否则使用动态内存分配不是一个好主意。仅使用静态数组就更容易、更安全。

于 2010-01-25T00:09:21.900 回答
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]. 标准保证了这一点:如果您malloc()有一些空间,则该空间是连续的。您为 4 个指针分配了空间,这四个指针的地址彼此相邻。您可以通过执行以下操作更清楚地看到这一点:

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

这应该打印 1。

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

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

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

于 2010-01-25T00:55:33.990 回答