为什么malloc(1)用于存储4字节整数?

问题描述 投票:1回答:7

根据我的理解,malloc(x)返回一个x字节长的内存块。

所以要存储一个4字节的整数,我会这样做:

    int *p = (int *)malloc(4);
    *p = 100;

因为sizeof(int)为我返回4。

但是,如果我这样做:

    int *p = (int *)malloc(1);
    *p = 100;

它似乎完全相同,没有存储值的问题。

为什么malloc()请求的内存量似乎不重要? 4字节整数不应该需要malloc(4)吗?

c memory malloc
7个回答
4
投票

如果这适用于您的情况,它只是偶然工作,并不保证工作。这是未定义的行为(比较this SO question),一切都可能发生。

你期望发生什么?你的程序崩溃了?

如果你更频繁地调用mallocfree,那可能仍然会发生。 malloc通常比请求多占用一些字节,并使用额外的空间进行管理(所有内存块的链表,内存块的大小)。如果你在分配的块之前或之后写了一些字节,那么你很可能会混淆内部管理结构,随后mallocfree会崩溃。

如果malloc内部总是分配最少n个字节,那么如果访问字节n + 1,程序可能只会崩溃。此外,操作系统通常仅基于页面保护存储器。如果一个页面的大小为512字节,并且您的malloc-ed字节位于页面的中间,那么您的进程可能能够读写页面的其余部分,并且只会在访问下一个内存页面时崩溃。但要记住:即使这有效,也是未定义的行为。


2
投票

C编程语言使您能够用脚射击自己。

它故意给程序员带来负担,他们应该知道他们在做什么。从广义上讲,原因是要实现性能,可读性和可移植性。

代码的行为未定义。如果要求1个字节,则期望只返回一个可用字节。事实上,操作系统和C运行时库似乎给你的回馈不仅仅是一个好奇的特点。

在其他情况下,编译器可能只是吃你的猫。

最后,在sizeof的调用中使用malloc而不是硬编码int类型的大小:在许多系统上sizeof(int)是2,4是常见的,并且标准允许所有大于1的值。在您的情况下,使用sizeof(int)sizeof(*p)是可能的。有些人喜欢后者,因为那时你不会在sizeof调用中硬编码变量的类型,所以要防止可能的变量类型更改。 (注意sizeof(*p)是编译时可评估的并且使用静态类型信息;因此它可以在p本身“存在”之前使用,如果你明白我的意思。)


2
投票

它似乎完全相同,没有存储值的问题。

您使用代码调用未定义的行为,因此您无法确定它是否有效。为了为整数分配内存,你应该这样做:

int *p;
p = malloc(sizeof (*p) ); //you can use sizeof(*p) as p is already declared and here you use the size of its content, which is actually the size of an int
if (p != NULL)
    *p = 100;

1
投票

为此,我们应该知道malloc函数如何在内部工作。为了动态分配内存,每个操作系统都使用系统调用。我们可以使用这些系统调用动态分配内存。这些系统调用因操作系统而异。

因此,一个操作系统的系统调用可能不适用于其他操作系统。而且如果我们使用系统调用动态分配内存,那么我们的程序将取决于平台。所以为了避免这种依赖,我们使用malloc函数。现在,malloc函数负责根据OS进行适当的系统调用以动态分配内存。

因此malloc本身会调用系统调用,这将是一个非常缓慢的过程,因为每次我们要求动态内存时它都必须使用系统调用。为了避免这种情况,每当我们请求动态内存时,它通常会分配额外的内存,以便下次可以避免系统调用,并且可以使用先前分配的内存的剩余块。这就是为什么你的程序正在运行,因为malloc正在分配额外的内存。


0
投票

通常malloc的实现方式是它分配的内存大小不小于段落大小等于16个字节。所以当你需要例如4字节的内存时,malloc实际上分配了16个字节。但是,此行为未在C标准中描述,您可能不会依赖它。结果,这意味着您显示的程序具有未定义的行为。


0
投票

我认为这是因为填充,即使你正在调用malloc(1)填充字节随内存。请检查此链接http://www.delorie.com/gnu/docs/glibc/libc_31.html


0
投票

malloc,因为来自C运行时或OS内核的所有内存块分配函数都针对内存访问和对象对齐进行了优化。

此外,malloc具体地说,在分配的空间前分配一个隐藏的控制块来跟踪分配(所需空间,分配空间等)。

malloc还必须保证所分配的内存地址适合任何存储对象,这意味着该块将在8,16,32甚至64或128字节边界上启动,具体取决于处理器和硬件(通常是硬件)特殊MMU)。边界也取决于访问速度,某些处理器具有不同的行为,具有不同的存储器访问(1,2,4,8,...字节)和地址边界。这种约束驱动malloc代码规范和分配器逻辑内存块分区。

在实际的一面让我们考虑一个X86处理器的分配器,它通常会返回一个在8字节边界(32位代码)上对齐的块,这对于int,float和even double都很有用。为此,malloc将可用内存竞技场划分为最小分配空间的“块”。当您分配甚至1个字节时,该函数至少分配一个块。最终这个块可以承载一个整数,甚至是一个double,但它依赖于实现,并且你不能认为它是确定性的,因为在同一函数的未来版本中,行为可以改变。

现在,我希望,这很清楚,因为您的代码似乎有效,请记住这是Undefined-Behavior,您必须保留它。 IT现在可以工作,而不是下一次修订,它可能会在某些硬件上崩溃,而不会在另一台处理器或机器上崩溃。

© www.soinside.com 2019 - 2024. All rights reserved.