为什么在C中使用malloc时指定大小?

请使用以下代码:

int *p = malloc(2 * sizeof *p); p[0] = 10; //Using the two spaces I p[1] = 20; //allocated with malloc before. p[2] = 30; //Using another space that I didn't allocate for. printf("%d", *(p+1)); //Correctly prints 20 printf("%d", *(p+2)); //Also, correctly prints 30 //although I didn't allocate space for it 

使用malloc(2 * sizeof *p)我为两个整数分配空间,对吧? 但是如果我将int添加到第三个位置,我仍然可以正确分配并可检索。

所以我的问题是, 为什么在使用malloc时指定大小

简单的逻辑:如果你没有停在合法的停车位,没有什么可能发生,但偶尔你的车可能会被拖走,你可能会被罚款。 而且,有时候,当你试图找到你的汽车被拖走的地方时,你可能会被一辆卡车撞倒。

malloc为您提供尽可能多的合法停车位。 您可以尝试在其他地方停车,它似乎可以工作,但有时它不会。

对于这样的问题, C FAQ的Memory Allocation部分是一个有用的参考咨询。 见7.3b 。

在相关(幽默)的说明中,另请参阅ART的bloopers列表。

C请让你自己开枪。 你刚刚在堆上使用了随机内存。 带来不可预见的后果。

免责声明:我上一次真正的C编程是在大约15年前完成的。

让我举一个类比为什么这个“有效”。

我们假设您需要绘制一幅图纸,这样您就可以取出一张纸,将其平放在桌子上,然后开始绘图。

不幸的是,这篇论文还不够大,但是你,而不是关心或不注意,只是继续绘制你的绘画。

完成后,你退后一步,看看你的绘图,它看起来很好,完全按照你的意思,完全按你的绘制方式。

直到有人出现并拿起他们在你到达之前留在桌子上的那张纸。

现在有一块画不见了。 你在那个人的论文上画的那篇文章。

此外,那个人现在在他的纸上有你的画作,可能会弄乱他想要在纸上的任何东西。

因此,虽然您的内存使用似乎可行,但它只会这样做,因为您的程序已完成。 在运行一段时间的程序中留下这样的错误,我可以保证你会得到奇怪的结果,崩溃和诸如此类的东西。

C就像类固醇上的电锯一样。 几乎没有什么是你做不到的。 这也意味着你需要知道自己在做什么,否则在你知道它之前,你会直视树和脚。

你有幸(非)幸运。 访问p [3]是未定义的,因为您没有为自己分配该内存。 读取/写出数组的结尾是C程序以神秘方式崩溃的方式之一。

例如,这可能会改变通过malloc分配的其他变量中的某些值。 这意味着它可能会在以后崩溃,并且很难找到覆盖数据的(无关的)代码。

更糟糕的是,您可能会覆盖其他一些数据,可能不会注意到。 想象一下这意外地覆盖了你欠某人的钱;-)

实际上, malloc并没有为你的第三个整数分配足够的空间,但你得到了“幸运”,你的程序没有崩溃。 你只能确定malloc已经准确地分配了你所要求的,而不是更多。 换句话说,您的程序写入了一块未分配给它的内存。

所以malloc需要知道你需要的内存大小,因为它不知道你最终会对内存做什么,你计划写入内存的对象数等等……

这一切都可以追溯到C让你自己射中脚。 仅仅因为你可以这样做,并不意味着你应该这样做。 除非你使用malloc专门分配它,否则p + 3的值绝对不能保证你放在那里。

试试这个:

 int main ( int argc, char *argv[] ) { int *p = malloc(2 * sizeof *p); int *q = malloc(sizeof *q); *q = 100; p[0] = 10; p[1] = 20; p[2] = 30; p[3] = 40; p[4] = 50; p[5] = 60; p[6] = 70; printf("%d\n", *q); return 0; } 

在我的机器上,它打印:

50

这是因为你覆盖了为p分配的内存,并在q上踩踏。

请注意,由于对齐限制,malloc可能不会将p和q放在连续的内存中。

内存被表示为可以存储数字的可枚举的连续行槽.Malloc函数使用其中一些插槽来获取自己的跟踪信息,有时还会返回大于所需数量的插槽,以便以后返回时它并没有停留在一小块不可用的内存中。 你的第三个int要么登陆mallocs自己的数据,要么退回到返回的chunk中的空白区域,要么是malloc从操作系统请求的未决内存区域,但尚未分配给你。

根据平台的不同,p [500]也可能“有效”。

使用malloc() ,您接受与运行时库的合同,在该合同中您同意请求尽可能多的内存,并且它同意将其提供给您。 这是朋友之间所有语言的握手协议,经常让人们陷入困境。 当您访问分配范围之外的地址时,您违反了您的承诺。

此时,您已经请求标准调用“未定义行为”,并且允许编译器和库做任何响应。 即使看起来“正确”工作也是允许的。

非常不幸的是,它经常正常工作,因为这个错误可能很难编写测试用例来捕获。 测试它的最佳方法是将malloc()替换为跟踪块大小限制的实现,并在每次机会时积极测试堆的健康状况,或者使用valgrind之类的工具来监视程序的行为。 “外部”并发现滥用缓冲存储器。 理想情况下,这种滥用会提前失败并大声失败。

使用接近原始分配的元素经常成功的一个原因是分配器经常给出与对齐保证的方便倍数相关的块,并且通常在开始之前的一个分配结束时产生一些“备用”字节。下一个。 但是,分配器通常存储在这些字节附近管理堆本身所需的关键信息,因此超出分配可能导致malloc()自身成功进行第二次分配所需的数据被破坏。

编辑: OP修正了*(p+2)p[1]混淆的边题,所以我编辑了我的答案以放弃这一点。

你要求两个整数的空间。 p [3]假设你有4个整数的空间!

===================

您需要告诉malloc您需要多少,因为它无法猜测您需要多少内存。

malloc可以做任何想做的事情,只要它至少返回你要求的内存量。

这就像在餐馆里找个座位一样。 您可能会获得比您需要的更大的桌子。 或者你可能会和其他人坐在一张桌子旁。 或者你可能会得到一张有一个座位的桌子。 只要您获得单一席位,Malloc就可以随心所欲地做任何事情。

作为使用malloc的“合同”的一部分,您必须永远不要引用超出您要求的内存,因为您只能保证获得您要求的金额。

因为malloc()在BYTES中分配。 因此,如果要分配(例如)2个整数,则必须指定2个整数的字节大小。 可以使用sizeof(int)找到整数的大小,因此2个整数的字节大小为2 * sizeof(int)。 把这一切放在一起,你得到:

 int * p = malloc(2 * sizeof(int)); 

注意:鉴于上面只为两个整数分配空间,你在分配第三个时非常顽皮。 你很幸运它没有崩溃。 🙂

因为malloc在堆上分配空间,这是您的程序使用的动态分配的内存的一部分。 然后,底层操作系统会为您的程序提供所需的数量(或者如果你最终得到一些错误,这意味着你总是应该检查malloc的错误状态的返回),它会使用一些虚拟内存映射到物理内存(即芯片)除非我们正在编写操作系统,否则涉及复杂事物(如分页)的聪明魔术我们不想深入研究。

当你使用*(p + 3)时,即使使用2 * sizeof(* p),你也会超出范围,因此你正在访问一个无效的内存块,非常适合seg故障。

你指定了b / c的大小,否则,该函数不知道堆内存中有多大的块要分配给你的程序用于该指针。

给malloc()的大小的原因是内存管理器跟踪为系统上的每个进程分配了多少空间。 这些表有助于系统知道谁分配了多少空间,以及哪些地址是free()能够的。

其次,c允许你随时写入ram的任何部分。 内核可能会阻止您写入某些部分,从而导致保护错误,但没有任何东西阻止程序员尝试。

第三,在所有可能的情况下,malloc()第一次可能不会简单地为您的进程分配8个字节。 这是依赖于实现的,但是内存管理器更有可能为您的使用分配一个完整的页面,因为它更容易分配页面大小块….然后后续的malloc()将进一步划分以前的malloc( )编辑页面。

正如大家所说,你正在写入实际没有分配的内存,这意味着可能会发生覆盖你的数据的事情。 为了certificate这个问题,你可以尝试这样的事情:

 int *p = malloc(2 * sizeof(int)); p[0] = 10; p[1] = 20; p[2] = 30; int *q = malloc(2 * sizeof(int)); q[0] = 0; // This may or may not get written to p[2], overwriting your 30. printf("%d", p[0]); // Correctly prints 10 printf("%d", p[1]); // Correctly prints 20 printf("%d", p[2]); // May print 30, or 0, or possibly something else entirely. 

没有办法保证你的程序会在p [2]为q分配空间。 实际上它可能选择一个完全不同的位置。 但对于像这样的简单程序,它似乎很可能,如果它确实在p [2]的位置分配q,它将清楚地表明超出范围的错误。

做:

 int *p = malloc(2 * sizeof(*p)); // wrong (if type is something greater than a machine word) [type] *p = malloc(2 * sizeof([type])); // right.