C 理解堆中的内存分配
我试图理解如何使用malloc在堆上分配内存,但遇到了以下观察结果,无法理解其背后的原因。如果有人能解释,那就太好了 首先,让我们看看我写的代码:C 理解堆中的内存分配,c,memory-management,heap-memory,C,Memory Management,Heap Memory,我试图理解如何使用malloc在堆上分配内存,但遇到了以下观察结果,无法理解其背后的原因。如果有人能解释,那就太好了 首先,让我们看看我写的代码: #include<stdio.h> #include<stdlib.h> void print_int_heap(unsigned int *ptr, int len) { printf("PREV_SIZE: [%08x] SIZE: [%08x] MEM: [%08x] for INT malloc(%d)\
#include<stdio.h>
#include<stdlib.h>
void print_int_heap(unsigned int *ptr, int len)
{
printf("PREV_SIZE: [%08x] SIZE: [%08x] MEM: [%08x] for INT malloc(%d)\n", *(ptr-2), *(ptr-1), ptr, len);
}
void print_char_heap(char *ptr, int len)
{
printf("PREV_SIZE: [%08x] SIZE: [%08x] MEM: [%08x] for CHAR malloc(%d)\n", *(ptr-2), *(ptr-1), ptr, len);
}
int main() {
unsigned int *ptr1 = malloc(20);
print_int_heap(ptr1, 20);
char *ptr2 = malloc(20)
print_char_heap(ptr2, 20);
return 0;
}
我可以理解int-malloc的输出,但我不理解为什么char-malloc的chunk大小值为0 来自DENNIS M.RITCHIE的C编程语言 而不是从编译的固定大小数组中进行分配, malloc将根据需要从操作系统请求空间。因为程序中的其他活动也可能需要空间,而不需要 调用此分配器时 malloc可能不是连续的。因此,它的空闲存储被保存为空闲块的列表。每个块包含一个大小,一个 指向下一个块的指针,以及 空间本身。这些块按存储地址递增的顺序保存,最后一个块(最高地址)指向第一个。这个 malloc()返回的块如下所示
points to
next free
block
|
---------------------------------------
| | size | |
---------------------------------------
| | |..address returned to the user
(ptr-2) (ptr-1) ptr -->
LSB MSB
这里
*(ptr-2)
打印“下一个空闲块”
内的值,如上图所示,*(ptr-1)
打印“大小”
块内的值,即分配了多少内存,&ptr
打印用户返回的地址。注意这里的ptr
类型是unsigned int*
所以*(ptr-2)
意味着从2*sizeof(int)
字节访问数据,就在ptr
点之前
这里呢
void print_char_heap(char *ptr, int len){
printf("PREV_SIZE: [%08x] SIZE: [%08x] MEM: [%08x] for CHAR malloc(%d)\n", *(ptr-2), *(ptr-1), ptr, len);
}
访问*(ptr-1)
ptr
类型为char*
意味着当您执行*(ptr-1)
操作时,它将从sizeof(char)
字节访问数据,就在ptr
点之前
在动态分配内存时使用也更好&只需运行
valgrind --leak-check=full -v ./your_exe
并分析valgrind
的消息。例如,它可能会显示如下内容
==3193== Invalid read of size 4
==3193== at 0x8048459: print_int_heap
==3193== Invalid read of size 4
==3193== at 0x8048461: print_int_heap
当您对指针执行算术运算时,算术运算是以指针指向的对象的大小为单位完成的。因此,使用
char*ptr
,ptr-1
从ptr
中的地址减去1个字节。但是使用unsigned int*ptr
,ptr-1
从ptr
中的地址减去sizeof(int)
因此,在这两个函数中,不能减去相同数量的字节来获取块的堆簿记数据
此外,当取消引用指针时,它只访问指针数据类型中的字节数。因此在print\u int\u heap()
中,*(ptr-1)
返回一个无符号int
,而在print\u char\u heap()
中返回一个字符
您可能只需要编写一个print\u heap()
函数,并将参数强制转换为调用程序中的适当类型。如果ptr
是int*
,*(ptr-1)
引用的sizeof(int)
字节就在ptr
引用的前面。这通常是一个32位的量,从ptr
之前的四个字节开始
类似地,如果它是一个char*
,*(ptr-1)
引用的sizeof(char)
字节正好在ptr
引用之前sizeof(char)
始终为1;通常,这将是ptr
值之前单个字节中的8位数量
这些显然是完全不同的事情
顺便说一下,您可以编写ptr[-1]
。但正如上面的分析所显示的,这并不是你想要的。您希望将ptr
强制转换为指向您认为在ptr
之前的对象的数据类型的指针,可能是uint32\t
从技术上讲,这都是未定义的行为,但是如果您的malloc
实现在分配之前存储了数据,并且您知道该数据的类型,那么我认为读取该数据是可以的。(尽管盯着系统函数的内部数据看总是有点粗鲁。)
请注意,并非所有的malloc
实现都做相同的事情。您很可能会在其他地方找到一个存储长度的数据库,或者根本不存储长度的数据库。他显然是在试图访问malloc的依赖于实现的簿记数据。它是UB这一事实可能与此无关,就像关于外壳代码漏洞的问题一样。是的。我怎么会忘了呢?。谢谢@Barmar让我修改一下。
next free
block (ptr-1)--> *(ptr-1) prints data from ? marked location.
| |
---------------------------------------
| | size |? | |
---------------------------------------
| | |..address returned to the user
ptr -->
LSB MSB
valgrind --leak-check=full -v ./your_exe
==3193== Invalid read of size 4
==3193== at 0x8048459: print_int_heap
==3193== Invalid read of size 4
==3193== at 0x8048461: print_int_heap