【问题标题】:What happens if I use malloc with the wrong size?如果我使用错误大小的 malloc 会发生什么?
【发布时间】:2013-04-01 05:20:17
【问题描述】:

我正在尝试了解有关内存分配的更多信息,因此我在下面编写了一些测试代码,看看如果我尝试分配比我需要的更小的内存会发生什么。

#include <stdlib.h>
#include <stdio.h>

typedef struct {
    char *message;
    int number;
} Object;

int main(int argc, char *argv[]) {
    Object *obj = malloc(sizeof(Object) - 8);   
    printf("The size of the struct is: %ld\n", sizeof(Object));
    printf("The size of what was allocated is: %ld\n", sizeof(*obj));

    obj->message = "Hello there! My name is Chris!";
    obj->number = 435543;

    puts(obj->message);
    printf("%d\n", obj->number);

    free(obj);

    return 0;
}

首先,sizeof(*obj) 是查看在这种情况下实际分配了多少内存的正确方法吗?其次,为什么即使我没有分配足够的空间,我仍然可以为结构对象赋值?

我的操作系统是 Ubuntu 12.10 64bit,编译器是 gcc 4.7.2

这是 valgrind 的输出:

==14257== Memcheck, a memory error detector
==14257== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==14257== Using Valgrind-3.7.0 and LibVEX; rerun with -h for copyright info
==14257== Command: ./ex
==14257== 
The size of the struct is: 16
The size of what was allocated is: 16
==14257== Invalid write of size 4
==14257==    at 0x400640: main (ex.c:15)
==14257==  Address 0x51f1048 is 0 bytes after a block of size 8 alloc'd
==14257==    at 0x4C2B3F8: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14257==    by 0x400604: main (ex.c:10)
==14257== 
Hello there! My name is Chris!
==14257== Invalid read of size 4
==14257==    at 0x40065A: main (ex.c:18)
==14257==  Address 0x51f1048 is 0 bytes after a block of size 8 alloc'd
==14257==    at 0x4C2B3F8: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==14257==    by 0x400604: main (ex.c:10)
==14257== 
435543
==14257== 
==14257== HEAP SUMMARY:
==14257==     in use at exit: 0 bytes in 0 blocks
==14257==   total heap usage: 1 allocs, 1 frees, 8 bytes allocated
==14257== 
==14257== All heap blocks were freed -- no leaks are possible
==14257== 
==14257== For counts of detected and suppressed errors, rerun with: -v
==14257== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2)

【问题讨论】:

  • 尝试使用 valgrind 查看所有内存分配。
  • 您正在进入未定义行为的领域。未定义的行为包括程序运行良好。

标签: c malloc


【解决方案1】:

在这种特定情况下,您可能会尝试分配 0 个字节,因为在大多数 32 位编译器中,sizeof(Object) 是 8。 malloc(0) 将返回 NULL,因为它分配的大小无效,尝试写入地址 NULL 肯定会使您的应用程序崩溃。

但是假设您成功分配了 4 个字节,并尝试在其中写入 8 个字节。在单线程应用程序中,它应该可以正常工作,因为尽管您将写入未分配的内存空间,但它不会完全写入虚拟内存中丢失的某个疯狂地址。

但是,如果你这样做:

Object* a = (Object*)malloc(4);
Object* b = (Object*)malloc(4);

ab 可能是在意式分配的。这意味着,在大多数 32 位编译器中,写入 a-&gt;number 将用相同的值覆盖 b-&gt;message,反之亦然,因为两者都将尝试将信息存储到内存中的相同空间。

【讨论】:

  • malloc(0) 可能会返回一个非 NULL 指针,并且取消引用该指针会导致未定义的行为(参见 C99 的附件 J.2)。所以,它可能会崩溃,也可能不会崩溃,它是未定义的。
  • 我不明白。我在64位系统上。 Valgrind 说我分配了 8 个字节,因为 struct 的大小是 16。但是第二段帮助我理解了很多。
  • 结构大小取决于编译器,而不是系统。 64 位系统可以运行将使用 32 位内存地址的 32 位应用程序。在 32 位应用程序sizeof 中,任何指针类型都是 4 字节。 int 通常也是 4 个字节,但在某些旧编译器中它可以有 2 个字节,在 64 位编译器中最多有 8 个字节,这意味着您的结构可以有 6、8、12 或 16 个字节,具体取决于这些因素。
【解决方案2】:

你做这种探索很好。大多数标准 c 库都包含非标准扩展,允许您在分配内存后检查内存大小。如果您想了解更多关于malloc 是如何实现的,您应该在从 malloc 返回后调用它并查看标准库实际分配了多少内存。你可能会发现像malloc(1) 这样简单的东西可以返回一个相当大的内存块。

正如其他一些读者所指出的,如果您要在 32 位系统上重新编译,您可能会要求 malloc 在您的示例代码中分配零字节。它会很乐意遵守并返回NULL

【讨论】:

  • 很酷,但是为什么我仍然可以分配给结构成员并打印它们?
  • 你的结构大概有 12 个字节长。在从malloc(1)malloc(sizeof(*obj)) 的任何情况下,该库分配的字节数都可能超过结构的长度。
  • malloc(0) 可能返回 NULL,或者它可能返回指向必须释放但无法访问的零字节内存的有效指针。两者都是标准允许的。
【解决方案3】:

这取决于编译器和操作系统。在许多情况下,它最终会崩溃。绝对不推荐。也可能导致缓冲区溢出。

【讨论】:

    【解决方案4】:

    要回答有关 sizeof 的子问题: sizeof 根据您使用的类型产生结果(在 C 中,可变长度数组有单独的情况)。如果你写

    T* obj = malloc (any value);
    

    然后 sizeof (*obj) 只查看 *obj 的类型,即 T,并产生 T 类型的对象的大小。分配是否失败并且 obj 实际上为 NULL,或者是否您分配的字节数少于或多于 T 的大小,即使您根本没有调用 malloc 并且 obj 是一个未初始化的变量,也没有关系。

    【讨论】:

      猜你喜欢
      • 2014-05-03
      • 2017-09-10
      • 2019-03-20
      • 2013-10-26
      • 1970-01-01
      • 1970-01-01
      • 2020-03-26
      • 2019-09-10
      • 2018-01-05
      相关资源
      最近更新 更多