我正在使用以下代码创建新节点并将其插入到链接列表中,随后将其释放 .
// the node
struct node
{
int data;
struct node *next;
};
// returns a pointer to a new node with data
struct node *new_node(int data)
{
struct node *node;
node = malloc(sizeof(struct node));
node->data = data;
node->next = NULL;
return node;
}
// insert node at front of list
void push(struct node **head, int data)
{
struct node *node = new_node(data);
node->next = *head;
*head = node;
}
// free the list, deallocate each node's memory
void delete_list(struct node** head)
{
if(*head == NULL)
return;
struct node *next = NULL;
next = (*head)->next;
while(next != NULL)
{
next = (*head)->next;
// print address of the memory released
printf("Freeing %d\n", (int)*head);
free(*head);
*head = next;
}
}
现在,结构在我的机器中是8个字节(4字节 int
和4字节指针) . 现在,我对以下内容有点不确定,所以请帮助我:
-
当我按顺序调用
push()
时,内存是否连续分配?总是这样吗?我猜它不可能,因为堆中的内存可能是碎片化的 . -
假设分配的内存是连续的,那么它将间隔8个字节,因为
struct
的大小是8个字节 . 在我的机器上,当我打印释放的存储器的地址时,每次执行时打印的存储器地址相隔16个字节 . 为什么?Freeing 148025480 Freeing 148025464 Freeing 148025448 Freeing 148025432 Freeing 148025416 Freeing 148025400 Freeing 148025384 Freeing 148025368 Freeing 148025352 <empty list>
-
现在,如果没有为我们的整数数组连续分配内存(堆非常碎片,并且内存需求非常大),我们使用指针算法通过每次递增地址4来处理数组的每个元素(或者无论
int
的大小是什么,都不应该知道如何分配内存 . 操作系统会处理这个问题吗?
3 回答
每次调用
new_node
时,都会调用malloc()
.你不能(或不应该)预测
malloc()
会找到你的记忆 . 它取决于操作系统和运行时 .在特定操作系统上运行,在某些情况下,您可能会发现连续调用
malloc()
的分配是连续的 . 但是,该行为可能会在加载,内核更新,libc实现的更改或其他各种条件下发生变化 .您可以假设通过对
malloc()
的单个调用分配的内存块是连续的(至少,就您的程序看到的指针而言) . 你的程序不应该假设任何关于邻接的东西 .如果这真的困扰你,你可以在你自己的代码中负责更多的内存管理 - 而不是为每个节点调用
malloc()
,在开始时调用它并获得更大的内存块 . 对new_node的后续调用可以使用此块的一部分 . 如果你在那个块中耗尽空间,你可以malloc()
另一个块(可能不会与第一个块连续)或realloc()
来扩展(并可能移动)它 .你是否有好处才能解决这个问题 . Hotspot Java VM的作者基本上是这样做的 - 他们在执行开始时是一个很大的内存块,而不是在Java程序需要内存时调用
malloc()
和free()
,它使用自己的例程来分配该块的部分内容 .关于#2,调用
malloc
不完全连续的结果的一个原因可能是元数据:当你要求x
字节时,malloc
实现可能为内部簿记过程分配一些额外的内存(标记块的大小,指向其他免费积木等) . 因此,对8字节的请求实际上可能导致在内部分配16个字节,因此连续分配之间的16字节间隔 .如果使用
malloc
分配内存,则返回的内存将始终是连续的 .如果你两次调用
malloc
,则无法保证两个分配将彼此相邻放置,即使两个malloc
调用彼此相邻也是如此 .