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

时间:2009-08-06 19:47:37

标签: c memory-management 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 时指定尺寸?

17 个答案:

答案 0 :(得分:126)

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

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

对于诸如此类的问题,Memory Allocation section of the C FAQ是一个有用的参考咨询。请参阅7.3b

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

答案 1 :(得分:31)

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

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

答案 2 :(得分:27)

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

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

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

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

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

现在有一块图画遗失了。你在另一个人的论文上写的这篇文章。

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

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

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

答案 3 :(得分:13)

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

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

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

答案 4 :(得分:4)

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

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

答案 5 :(得分:4)

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

答案 6 :(得分:4)

试试这个:

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放在连续的内存中。

答案 7 :(得分:2)

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

答案 8 :(得分:2)

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

答案 9 :(得分:1)

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

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

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

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

修改:OP解决了*(p+2)p[1]感到困惑的副作用问题,所以我编辑了我的答案以放弃这一点。

答案 10 :(得分:1)

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

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

你需要告诉malloc你需要多少钱,因为它不能猜测你需要多少内存。

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

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

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

答案 11 :(得分:0)

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

答案 12 :(得分:0)

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

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

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

答案 13 :(得分:0)

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

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,它将清楚地演示超出范围的错误。

答案 14 :(得分:0)

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

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

答案 15 :(得分:0)

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

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

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

答案 16 :(得分:-2)

做:

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

[type] *p = malloc(2 * sizeof([type])); // right.