简体   繁体   English

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

[英]Why do you specify the size when using malloc in C?

Take the following code :采取以下代码:

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

With the line malloc(2 * sizeof *p) I am allocating space for two integers, right ?使用malloc(2 * sizeof *p)我正在为两个整数分配空间,对吗? But if I add an int to the third position, I still gets allocated correctly and retrievable.但是如果我在第三个位置添加一个int ,我仍然可以正确分配并且可以检索。

So my question is, why do you specify a size when you use malloc ?所以我的问题是,为什么在使用malloc时指定大小

Simple logic: If you do not park in a legal parking space, nothing might happen but occasionally your car might get towed and you might get stuck with a huge fine.简单的逻辑:如果你不把车停在合法的停车位,什么都不会发生,但偶尔你的车可能会被拖走,你可能会被巨额罚款。 And, sometimes, as you try to find your way to the pound where your car was towed, you might get run over by a truck.而且,有时,当您试图找到通往汽车被拖走的车站的路时,您可能会被卡车碾过。

malloc gives you as many legal parking spots as you asked. malloc为您提供尽可能多的合法停车位。 You can try to park elsewhere, it might seem to work, but sometimes it won't.您可以尝试将车停在别处,这似乎可行,但有时却行不通。

For questions such as this, the Memory Allocation section of the C FAQ is a useful reference to consult.对于此类问题, C FAQ内存分配部分是一个有用的参考参考。 See 7.3b .7.3b

On a related (humorous) note, see also a list of bloopers by ART .在相关(幽默)笔记中,另请参阅ARTbloopers列表。

C kindly let you shoot yourself in the head. C 好心让你朝自己的脑袋开枪。 You have just used random memory on the heap.您刚刚在堆上使用了随机内存。 With unforeseeable consequences.带来不可预见的后果。

Disclaimer: My last real C programing was done some 15 years ago.免责声明:我最后一次真正的 C 编程是在大约 15 年前完成的。

Let me give you an analogy to why this "works".让我给你一个类比,为什么这个“有效”。

Let's assume you need to draw a drawing, so you retrieve a piece of paper, lay it flat on your table, and start drawing.假设您需要画一幅画,因此您取出一张纸,将它平放在桌子上,然后开始画画。

Unfortunately, the paper isn't big enough, but you, not caring, or not noticing, just continue to draw your drawing.不幸的是,纸不够大,但你,不在乎,或者没有注意到,继续画你的画。

When done, you take a step back, and look at your drawing, and it looks good, exactly as you meant it to be, and exactly the way you drew it.完成后,您退后一步,看看您的绘图,它看起来不错,完全符合您的意图,并且完全符合您的绘制方式。

Until someone comes along and picks up their piece of paper that they left on the table before you got to it.直到有人走过来捡起他们留在桌子上的纸,然后才到达它。

Now there's a piece of the drawing missing.现在有一块图纸丢失了。 The piece you drew on that other person's paper.你在那个人的纸上画的那幅画。

Additionally, that person now has pieces of your drawing on his paper, probably messing with whatever he wanted to have on the paper instead.此外,那个人现在在他的纸上有你的画,可能会弄乱他想在纸上画的任何东西。

So while your memory usage might appear to work, it only does so because your program finishes.因此,虽然您的内存使用情况可能看起来有效,但它只是因为您的程序完成了。 Leave such a bug in a program that runs for a while and I can guarantee you that you get odd results, crashes and whatnot.在运行一段时间的程序中留下这样的错误,我可以向您保证,您会得到奇怪的结果、崩溃等等。

C is built like a chainsaw on steroids. C 的构建就像是在类固醇上的链锯。 There's almost nothing you cannot do.几乎没有什么是你做不到的。 This also means that you need to know what you're doing, otherwise you'll saw right through the tree and into your foot before you know it.这也意味着您需要知道自己在做什么,否则您会在不知不觉中看到树和脚。

You got (un)lucky.你有(不)幸运。 Accessing p[3] is undefined, since you haven't allocated that memory for yourself.访问 p[3] 是未定义的,因为您尚未为自己分配该内存。 Reading/writing off the end of an array is one of the ways that C programs can crash in mysterious ways.读/写数组的末尾是 C 程序可能以神秘方式崩溃的一种方式。

For example, this might change some value in some other variable that was allocated via malloc.例如,这可能会更改通过 malloc 分配的某些其他变量中的某些值。 That means it might crash later, and it'll be very hard to find the piece of (unrelated) code that overwrote your data.这意味着它可能会在稍后崩溃,并且很难找到覆盖数据的(无关)代码段。

Worse yet, you might overwrite some other data and might not notice.更糟糕的是,您可能会覆盖一些其他数据而可能不会注意到。 Imagine this accidentally overwrites the amount of money you owe someone ;-)想象一下,这不小心覆盖了您欠某人的金额;-)

In fact, malloc is not allocating enough space for your third integer, but you got "lucky" and your program didn't crash.事实上, malloc没有为您的第三个整数分配足够的空间,但是您“很幸运”并且您的程序没有崩溃。 You can only be sure that malloc has allocated exactly what you asked for, no more.您只能确定 malloc 已准确分配了您所要求的内容,不能再分配了。 In other words, your program wrote to a piece of memory that was not allocated to it.换句话说,您的程序写入了一块未分配给它的内存。

So malloc needs to know the size of the memory that you need because it doesn't know what you will end up doing with the memory, how many objects you plan on writing to the memory, etc...所以 malloc 需要知道你需要的内存大小,因为它不知道你最终会对内存做什么,你计划写入内存的对象数量等等......

This all goes back to C letting you shoot yourself in the foot.这一切都可以追溯到让你用脚射击自己的 C。 Just because you can do this, doesn't mean you should.仅仅因为您可以做到这一点,并不意味着您应该这样做。 The value at p+3 is definitely not guaranteed to be what you put there unless you specifically allocated it using malloc. p+3 的值绝对不能保证是你放在那里的值,除非你使用 malloc 专门分配它。

Try this:尝试这个:

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;
}

On my machine, it prints:在我的机器上,它打印:

50 50

This is because you overwrote the memory allocated for p, and stomped on q.这是因为您覆盖了为 p 分配的内存,并踩了 q。

Note that malloc may not put p and q in contiguous memory because of alignment restrictions.请注意,由于对齐限制,malloc 可能不会将 p 和 q 放在连续内存中。

Memory is represented as an enumerable contiguous line of slots that numbers can be stored in. The malloc function uses some of these slots for its own tracking info, as well as sometimes returning slots larger than what you need, so that when you return them later it isn't stuck with an unusably small chunk of memory.内存表示为可以存储数字的可枚举连续槽行。 malloc 函数使用其中一些槽作为其自己的跟踪信息,有时还会返回大于您需要的槽,以便稍后返回它们时它不会被无法使用的小块内存卡住。 Your third int is either landing on mallocs own data, on empty space leftover in the returned chunk, or in the area of pending memory that malloc has requested from the OS but not otherwise parcelled out to you yet.您的第三个 int 要么落在 mallocs 自己的数据上,要么落在返回块中剩余的空白空间上,要么落在 malloc 已从操作系统请求但尚未分配给您的待处理内存区域中。

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

You are asking for space for two integers.您要求两个整数的空间。 p[3] assumes that you have space for 4 integers! p[3] 假设您有 4 个整数的空间!

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

You need to tell malloc how much you need because it can't guess how much memory you need.您需要告诉 malloc 您需要多少内存,因为它无法猜测您需要多少内存。

malloc can do whatever it wants as long as it returns at least the amount of memory you ask for. malloc 可以做任何它想做的事情,只要它至少返回您要求的内存量。

It's like asking for a seat in a restaurant.这就像在餐厅要求座位。 You might be given a bigger table than you need.你可能会得到一张比你需要的更大的桌子。 Or you might be given a seat at a table with other people.或者,您可能会与其他人坐在一张桌子旁。 Or you might be given a table with one seat.或者你可能会得到一张只有一个座位的桌子。 Malloc is free to do anything it wants as long as you get your single seat.只要您获得单人席位,Malloc 就可以自由地做任何想做的事情。

As part of the "contract" for the use of malloc, you are required to never reference memory beyond what you have asked for because you are only guaranteed to get the amount you asked for.作为使用 malloc 的“合同”的一部分,您必须永远不要引用超出您要求的内存,因为您只能保证获得您要求的数量。

When using malloc() , you are accepting a contract with the runtime library in which you agree to ask for as much memory as you are planning to use, and it agrees to give it to you.使用malloc() ,您接受了与运行时库的合同,您同意在其中请求您计划使用的尽可能多的内存,并且它同意将其提供给您。 It is the kind of all-verbal, handshake agreement between friends, that so often gets people in trouble.这是朋友之间的那种全口头的握手协议,经常给人们带来麻烦。 When you access an address outside the range of your allocation, you are violating your promise.当您访问分配范围之外的地址时,您违反了您的承诺。

At that point, you have requested what the standard calls "Undefined Behavior" and the compiler and library are allowed to do anything at all in response.此时,您已请求标准称为“未定义行为”的内容,并且允许编译器和库执行任何响应。 Even appearing to work "correctly" is allowed.即使看起来“正确”工作也是允许的。

It is very unfortunate that it does so often work correctly, because this mistake can be difficult to write test cases to catch.非常不幸的是,它经常能正常工作,因为这种错误很难编写测试用例来捕捉。 The best approaches to testing for it involve either replacing malloc() with an implementation that keeps track of block size limits and aggressively tests the heap for its health at every opportunity, or to use a tool like valgrind to watch the behavior of the program from "outside" and discover the misuse of buffer memory.测试它的最佳方法包括用跟踪块大小限制的实现替换malloc()并在每一个机会积极测试堆的健康状况,或者使用像valgrind这样的工具来观察程序的行为“外部”并发现缓冲区内存的滥用。 Ideally, such misuse would fail early and fail loudly.理想情况下,这种滥用会提前失败并大声失败。

One reason why using elements close to the original allocation often succeeds is that the allocator often gives out blocks that are related to convenient multiples of the alignment guarantee, and that often results in some "spare" bytes at the end of one allocation before the start of the next.使用接近原始分配的元素通常会成功的一个原因是,分配器通常会给出与对齐保证的方便倍数相关的块,并且这通常会在开始之前的一次分配结束时产生一些“备用”字节下一个。 However the allocator often store critical information that it needs to manage the heap itself near those bytes, so overstepping the allocation can result in destruction of the data that malloc() itself needs to successfully make a second allocation.然而,分配器通常在这些字节附近存储管理堆本身所需的关键信息,因此超出分配可能会导致malloc()本身成功进行第二次分配所需的数据的破坏。

Edit: The OP fixed the side issue with *(p+2) confounded against p[1] so I've edited my answer to drop that point.编辑: OP 修复了*(p+2)p[1]混淆的附带问题,所以我编辑了我的答案以降低这一点。

When you use * (p+3), you're addressing out of bounds even with using 2*sizeof(* p), hence you're accessing an invalid memory block, perfect for seg faults.当您使用 * (p+3) 时,即使使用 2*sizeof(* p) 也会超出范围寻址,因此您正在访问无效的内存块,非常适合段错误。

You specify the size b/c otherwise, the function doesn't know how big of a block out of the heap memory to allocate to your program for that pointer.您指定大小 b/c 否则,该函数不知道堆内存中有多大的块分配给您的程序以用于该指针。

Because malloc() allocates in BYTES.因为 malloc() 以 BYTES 分配。 So, if you want to allocate (for example) 2 integers you must specify the size in bytes of 2 integers.因此,如果要分配(例如)2 个整数,则必须指定 2 个整数的字节大小。 The size of an integer can be found by using sizeof(int) and so the size in bytes of 2 integers is 2 * sizeof(int).可以使用 sizeof(int) 找到整数的大小,因此 2 个整数的字节大小为 2 * sizeof(int)。 Put this all together and you get:把这一切放在一起,你会得到:

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

Note: given that the above only allocates space for TWO integers you are being very naughty in assigning a 3rd.注意:鉴于上面只为两个整数分配空间,你在分配第三个时非常顽皮。 You're lucky it doesn't crash.你很幸运它没有崩溃。 :) :)

Because malloc is allocating space on the heap which is part of the memory used by your program which is dynamically allocated.因为 malloc 在堆上分配空间,这是动态分配的程序使用的内存的一部分。 The underlying OS then gives your program the requested amount (or not if you end up with some error which implies you always should check return of malloc for error condition ) of virtual memory which it maps to physical memory (ie. the chips) using some clever magic involving complex things like paging we don't want to delve into unless we are writing an OS.底层操作系统然后为您的程序提供请求的数量(或者如果您最终遇到一些错误,这意味着您总是应该检查 malloc 的返回是否有错误条件)的虚拟内存,它使用一些映射到物理内存(即芯片)巧妙的魔法涉及复杂的事情,例如分页,除非我们正在编写操作系统,否则我们不想深入研究。

As everyone has said, you're writing to memory that isn't actually allocated, meaning that something could happen to overwrite your data.正如每个人所说,您正在写入实际上并未分配的内存,这意味着可能会发生某些事情来覆盖您的数据。 To demonstrate the problem, you could try something like this:为了演示这个问题,你可以尝试这样的事情:

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.

There's no way to guarantee your program will allocate space for q at p[2].无法保证您的程序会在 p[2] 处为 q 分配空间。 It may in fact choose a completely different location.它实际上可能会选择一个完全不同的位置。 But for a simple program like this, it seems likely, and if it does allocate q at the location where p[2] would be, it will clearly demonstrate the out-of-range error.但是对于像这样的简单程序,它似乎很有可能,并且如果它确实在 p[2] 所在的位置分配了 q,它将清楚地证明超出范围的错误。

The reason for the size given to malloc() is for the memory manager to keep track of how much space has been given out to each process on your system.分配给 malloc() 大小的原因是为了让内存管理器跟踪系统上每个进程分配了多少空间。 These tables help the system to know who allocated how much space, and what addresses are free()able.这些表帮助系统知道谁分配了多少空间,以及哪些地址是 free()able。

Second, c allows you to write to any part of ram at any time.其次,c 允许您随时写入 ram 的任何部分。 Kernel's may prevent you from writing to certain sections, causing protection faults, but there is nothing preventing the programmer from attempting.内核可能会阻止您写入某些部分,从而导致保护错误,但没有什么可以阻止程序员尝试。

Third, in all likelyhood, malloc()ing the first time probably doesn't simply allocate 8 bytes to your process.第三,在所有可能的情况下,第一次 malloc() 可能不会简单地为您的进程分配 8 个字节。 This is implementation dependent, but it is more likely for the memory manager to allocate a full page for your use just because it is easier to allocate page size chunks....then subsequent malloc()'s would further divide the previously malloc()ed page.这是依赖于实现的,但内存管理器更有可能分配一个完整的页面供您使用,因为它更容易分配页面大小的块......然后后续的 malloc() 将进一步划分以前的 malloc( ) 页面。

Do :做 :

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在 C 中,当使用 malloc 创建二维数组时,为什么在传递给函数时不需要指定二维数组大小? - In C why do I NOT need to specify 2D array size when passing into function when the 2D array is created with malloc? 为什么输入超过 malloc 大小的字母没有错误? - Why is there no error when you input letters more than malloc size? ANSI C在创建结构时是否必须使用malloc()? - ANSI C do you have to use malloc() when creating a struct? 当我们可以使用arr [size]声明数组时,为什么要在c中使用malloc函数呢? - why use malloc function in c when we can declare arrays using arr[size] ,taking input from the user for size? C-在使用具有大型参数的malloc时出现分段错误 - C - Segmentation Fault when using malloc with large size paramater 为什么我们在 C 中指定 size_t 数组大小而不是仅使用整数? 这样做有什么好处? - Why do we specify size_t array size in C rather than just using integers? And what are the advantages to do so? 为什么以及何时在C中无法使用malloc()? - Why and when malloc() will not be available in C? 使用CUDA时,为什么malloc()和calloc()似乎不起作用? - Why do malloc() and calloc() not seem work when using CUDA? 为什么这个malloc包装器会按照所请求内存的大小执行此操作? size =(size + 3)&〜3; - Why does this malloc wrapper do this to the size of the requested memory? size = (size + 3) & ~3; 使用Malloc在C中分配数组大小 - Using Malloc to allocate array size in C
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM