确保我正在写入我在 C 中拥有的内存

Making sure I'm writing to memory I own in C

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

struct Person
{
    unsigned long age;
    char name[20];
};

struct Array
{
    struct Person someone;
    unsigned long used;
    unsigned long size;
};

int main()
{
    //pointer to array of structs
    struct Array** city;
    //creating heap for one struct Array
    struct Array* people=malloc(sizeof(struct Array));
    city=&people;

    //initalizing a person
    struct Person Rob;
    Rob.age=5;
    strcpy(Rob.name,"Robert");

    //putting the Rob into the array
    people[0].someone=Rob;

    //prints Robert
    printf("%s\n",people[0].someone.name);
    //another struct
    struct Person Dave;
    Dave.age=19;
    strcpy(Dave.name,"Dave");
    //creating more space on the heap for people. 
    people=realloc(people,sizeof(struct Array)*2);
    //How do I know that this data is safe in memory from being overwritten? 
    people[1].someone=Dave;
    //prints Dave
    printf("%s\n",people[1].someone.name);
    //accessing memory on the heap I do not owe?
    people[5].someone=Rob;
    //prints "Robert" why is this okay? Am I potentially overwriting memory?
    printf("%s\n",people[5].someone.name);

    return 0;
}

在上面的代码中,我尝试创建一个指向动态结构数组的指针,不确定我是否成功了那部分,但我主要关心的是我使用 malloc 在堆上创建 space array 'people.' 稍后在代码中我创建了另一个 struct Person 并使用 realloc 在堆上为 'people.' 创建了更多 space 然后我在我认为我给的内存之外写入内存 space 通过 'people[5].someone=Rob;.' 这仍然有效,因为我可以访问该内存位置的值。我的问题是为什么这行得通?我是否有可能通过写入我没有专门为人们定义的内存来覆盖内存?我真的正确地使用了 malloc 和 realloc 吗?正如我所听说的那样,有一些方法可以测试他们是否在另一个 post 中取得了成功。我是 C 的新手,所以如果我的假设或术语不正确,请纠正我。

首先,永远不要忘记释放内存。

// NEVER FORGET TO FREE YOUR MEMORY
free(people);

至于这部分

//accessing memory on the heap I do not owe?
people[5].someone=Rob;
//prints "Robert" why is this okay? Am I potentially overwriting memory?
printf("%s\n",people[5].someone.name);

你只是幸运(或者在我看来是不幸的,因为你没有看到你正在做的逻辑错误)。

这是未定义的行为,因为你有两个单元格,但你访问第 6 个单元格,你就超出了范围。

我不是C专家,连中级都不是,大部分时间我用C#编程,所以可能会有一些错误。

现代操作系统有一种称为内存管理器的特殊机制。使用该机制,我们可以要求 OS 给我们一些内存。在 Windows 中有一个特殊的函数 - VirtualAlloc。这是一个非常强大的功能,您可以在 MSDN 上阅读更多相关信息。

它工作得非常好,为我们提供了我们需要的所有内存,但有一个小问题 - 它为我们提供了整个物理页面 (4KB)。好吧,其实这不是什么大问题,您可以像使用 malloc 分配内存一样使用这块内存。不会有错误的。

但这是一个问题,因为如果我们,例如,使用 VirtualAlloc 分配一个 10 字节的块,它实际上会给我们 4096 字节的块,因为内存大小四舍五入到页面大小边界。所以 VirtualAlloc 分配了一个 4KB 的内存块,但我们实际上只使用了其中的 10 个字节。剩下的4086个是"gone"。如果我们创建第二个 10 字节的数组,VirtualAlloc 会给我们另一个 4096 字节的块,所以两个 10 字节的数组实际上会占用 8KB 的 RAM。

为了解决这个问题,每个 C 程序都使用 malloc 函数,它是 C 运行时库的一部分。它使用 VirtualAlloc 和 returns 指针分配一些 space 到它的部分。例如让我们 return 到我们以前的数组。如果我们使用 malloc 分配 10 字节数组,运行时库将调用 VirtualAlloc 分配一些 space,并且 malloc 将 return 指针指向它的开头。但是如果我们第二次分配 10 字节数组,malloc 将不会使用 VirtualAlloc。相反,它将使用已经分配的页面,我的意思是它的空闲 space。在分配第一个数组后,我们的内存块中有 4086 字节未使用的 space。所以 malloc 会明智地使用这个 space 。在这种情况下(对于第二个数组)它将 return 指向 "address of chunk" + 10 (这是一个内存地址)的指针。

现在我们可以分配大约 400 "ten byte arrays",如果我们使用 malloc,它们将只占用 4096 字节。使用 VirtualAlloc 的简单方法需要 400 * 4096 bytes = 1600KB,与使用 malloc 的 4096 字节相比,这是一个相当大的数字。

还有另一个原因 - 性能,因为 VirtualAlloc 是一项非常昂贵的操作。但是,如果分配的块中有空闲 space,malloc 将执行一些指针数学运算,但如果没有分配任何空闲 space,它将调用 VirtualAlloc。其实比我说的要复杂的多,不过我想这就足以说明原因了。

好的,让我们return回答问题。您为 Array 数组分配内存。让我们计算它的大小:sizeof(Person) = sizeof(long) + sizeof(char[20]) = 4 + 20 = 24 bytessizeof(Array) = sizeof(Person) + 2 * sizeof(long) = 24 + 8 = 32 bytes。 2 个元素的数组将占用 32 * 2 = 64 个字节。因此,正如我之前所说,malloc 将调用 VirtualAlloc 来分配一些内存,并且它将 return 一个 4096 字节的页面。因此,例如,假设块的起始地址为 0。应用程序可以在分配页面时修改 0 到 4096 之间的任何字节,并且不会出现任何页面错误。什么是数组索引array[n]?它只是数组的基数和计算为 array + n * sizeof(*array) 的偏移量的总和。在 person[5] 的情况下,它将是 0 + sizeof(Array) * 5 = 0 + 5 * 64 = 320 bytes。明白了!我们仍然在块的边界,我的意思是我们访问现有的物理页面。如果我们试图访问一个不存在的虚拟页面,就会发生页面错误,但在我们的例子中它存在于地址 320(我们假设从 0 到 4096)。访问未分配的 space 是很危险的,因为它会导致许多未知的后果,但我们实际上可以做到!

这就是您没有得到任何 Access Violation at **** 的原因。但实际上更糟。因为如果您尝试访问零指针,您将遇到页面错误并且您的应用程序将崩溃,因此您将在调试器或其他工具的帮助下知道问题的原因。但是如果你溢出了缓冲区并且你没有得到任何错误,你会在寻找问题的原因时发疯。因为很难找到这种错误。你甚至可能没有意识到它。所以永远不要溢出堆中分配的缓冲区。其实微软的C Runtime有一个特殊的"debug"版本的malloc可以在运行时发现这些错误,但是你需要用"DEBUG"配置编译应用程序。另外,还有一些特殊的东西,比如Valgrind,但是我对这些东西有点经验。

好吧,我写了很多,对不起我的英语,我还在学习。希望对你有所帮助。