C 理解堆中的内存分配

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)\

我试图理解如何使用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)\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