#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;
}
Run Code Online (Sandbox Code Playgroud)
在上面的代码中,我尝试创建一个指向动态结构数组的指针,不确定我是否在该部分成功,但我主要担心的是我使用malloc在堆上为数组'people'创建空间.稍后在代码中我创建另一个struct Person并使用realloc在堆上为'people'创建更多空间.然后我通过做'人[5] .someone = Rob;来写我以为我给了空间的记忆.这仍然有效,因为我可以访问该内存位置的值.我的问题是为什么这有效?我是否可能通过写入我没有专门为人们定义的内存来覆盖内存?我实际上正确使用malloc和realloc吗?正如我所听到的那样,如果他们在另一篇文章中取得成功,就有可能进行测试.我是C的新手,所以如果我的假设或术语不合适,请纠正我.
我不是C的专家,甚至不是中级,大多数时候我都是用C#编程的,所以有些错误可能存在.
现代操作系统有一种称为内存管理器的特殊机制.使用该机制,我们可以要求操作系统给我们一些内存.在Windows中有一个特殊功能 - VirtualAlloc.这是一个非常强大的功能,您可以在MSDN上阅读更多相关信息.
它工作得非常好,并为我们提供了所需的所有内存,但是有一点问题 - 它为我们提供了整个物理页面(4KB).好吧,实际上这不是一个大问题,你可以像使用malloc分配一样使用这个内存.没有错误.
但这是一个问题,因为如果我们使用VirtualAlloc分配一个10字节的块,它实际上会给我们4096字节的块,因为内存大小向上舍入到页面大小边界.因此VirtualAlloc分配了一个4KB的内存块,但实际上我们只使用了10个字节.其余的4086都"消失了".如果我们创建第二个10字节数组,VirtualAlloc将为我们提供另一个4096字节的块,因此两个10字节数组实际上将占用8KB的RAM.
为了解决这个问题,每个C程序都使用malloc函数,它是C运行时库的一部分.它使用VirtualAlloc分配一些空间并返回指向它的部分的指针.例如,让我们回到之前的数组.如果我们使用malloc分配10字节数组,运行时库将调用VirtualAlloc来分配一些空间,malloc将返回指向它开头的指针.但是如果我们第二次分配10字节数组,malloc就不会使用VirtualAlloc.相反,它将使用已经分配的页面,我的意思是它的自由空间.在分配了第一个数组之后,我们的内存块中有4086个字节的未使用空间.所以malloc将明智地使用这个空间.在这种情况下(对于第二个数组),它将返回指向"address of chunk" + 10(这是一个内存地址)的指针.
现在我们可以分配大约400个"十字节数组",如果我们使用malloc它们只需要4096个字节.使用VirtualAlloc的朴素方式400 * 4096 bytes = 1600KB,与使用malloc的4096字节相比,这是一个相当大的数字.
还有另外一个原因 - 性能,因为VirtualAlloc是一个非常昂贵的操作.但是,如果分配的块中有可用空间,malloc将执行一些指针数学运算,但如果没有任何空闲分配空间,它将调用VirtualAlloc.实际上它比我说的复杂得多,但我认为这足以解释原因.
好的,让我们回到这个问题.您为Array阵列分配内存.我们来计算它的大小:sizeof(Person) = sizeof(long) + sizeof(char[20]) = 4 + 20 = 24 bytes; sizeof(Array) = sizeof(Person) + 2 * sizeof(long) = 24 + 8 = 32 bytes.2个元素的数组将采用32*2 = 64字节.所以,正如我之前所说,malloc将调用VirtualAlloc来分配一些内存,它将返回一个4096字节的页面.因此,例如,让我们假设块开头的地址是0.应用程序可以修改从0到4096的任何字节,因为我们分配了页面,我们不会得到任何页面错误.什么是数组索引array[n]?它只是数组基数和计算的偏移量的总和array + n * sizeof(*array).如果是的person[5]话0 + sizeof(Array) * 5 = 0 + 5 * 64 = 320 bytes.疑难杂症!我们仍然处于大块的边界,我的意思是我们访问现有的物理页面.如果我们试图访问未存在的虚拟页面,则会发生页面故障,但在我们的情况下,它存在于地址320(我们假设为0到4096).访问未分配的空间是危险的,因为它可能导致许多未知的后果,但我们实际上可以做到!
这就是为什么你没有得到任何东西Access Violation at ****.但它实际上很多.因为如果您尝试访问零指针,您将收到页面错误,您的应用程序将崩溃,因此您将在调试器或其他东西的帮助下知道问题的原因.但是如果你超出缓冲区并且没有出现任何错误,那么在寻找问题的原因时你会发疯.因为找到这种错误真的很难.你甚至可以不了解它.因此,永远不要超过在HEAP中分配的缓冲器.实际上,Microsoft的C Runtime有一个特殊的"调试"版本的malloc,可以在运行时找到这些错误,但是你需要使用"DEBUG"配置编译应用程序.还有一些特别的东西,如Valgrind,
好吧,我写了很多,抱歉我的英语,我还在学习它.希望它会对你有所帮助.