【问题标题】:Interesting behaviour of malloc()malloc() 的有趣行为
【发布时间】:2017-02-07 20:10:42
【问题描述】:

这是我为GCC 编译器编写的代码。

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

int main()
{
    int *p;
    p = (int *)malloc(9 * sizeof(char));
    p[0] = 2147483647;
    p[1] = 2147483647;
    p[2] = 1025;
    printf("%d, %d, %d, %d\n", sizeof(char), *(p), *(p+1), *(p+2));
}

输出如下:

1, 2147483647, 2147483647, 1025

我的问题是,虽然我只为指针分配了 9 个字节,但它似乎使用了全部 12 个字节。如果我使用(char *) 而不是 (int *),结果是相同的(除非出现编译器警告)。 malloc()的分配是不是一圈圈?即它总是以指针的数据类型的倍数分配,而不管我们分配了什么?还是特定于实现?

【问题讨论】:

  • 应该按字节计算? PS - 你不需要演员表。
  • 你做了不合法的事情,没有人注意到。合法吗?
  • 谢谢。我更正了
  • 代码被破坏并且做了一些不应该做的事情。它没有造成坏事的事实只是一个巧合,可能会发生也可能不会发生。
  • @UmeshCG 如果您想知道为什么写到malloc()'d 缓冲区的末尾,请参阅stackoverflow.com/a/32683914/4756299,尽管这是未定义的行为,但有时似乎是无害的。

标签: c pointers malloc allocation


【解决方案1】:
    p = (int *)malloc(9 * sizeof(char));

如果整数大小为 4 个字节,您正在做的是 undefined behavior。因为您分配了 9 个字节并使用了 12 个。

强制转换没有任何效果,实际上你根本不应该在 C 中强制转换 malloc 的结果。

触发未定义行为的另一个地方是对sizeof 使用了错误的格式说明符,您应该在printf 中使用%zu 而不是%d(对于sizeof)。

【讨论】:

  • sizeof 的正确格式说明符是 "%zu",因为它是无符号的。
【解决方案2】:

您能够写入“额外”字节的事实并不意味着它们已分配给您;它只是意味着它们存在。你没有理由期望这些字节在未来不会“神奇地”改变,因为你没有分配它们,如果其他代码做了,可以改变它们(而且,与你的代码不同,这样做是合法的)。

【讨论】:

  • 你的意思是说它不合法,我很幸运程序没有崩溃?
  • @UmeshCG 我想说,不走运。
【解决方案3】:

正如其他人所提到的,您正在写入超出分配缓冲区的末尾。这会调用undefined behavior

未定义的行为意味着任何事情都可能发生。该程序可能会崩溃,它可能会产生意想不到的结果,或者(在这种情况下)它似乎可以正常工作。

进行看似无关的代码更改可能会使您的程序突然开始崩溃。

为了说明发生了什么,下面是在 valgrind 下运行程序的输出:

[dbush] valgrind /tmp/x1
==19430== Memcheck, a memory error detector
==19430== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==19430== Using Valgrind-3.5.0 and LibVEX; rerun with -h for copyright info
==19430== Command: /tmp/x1
==19430==
==19430== Invalid write of size 4
==19430==    at 0x40050E: main (x1.c:11)
==19430==  Address 0x4c18048 is 8 bytes inside a block of size 9 alloc'd
==19430==    at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==19430==    by 0x4004E9: main (x1.c:8)
==19430==
==19430== Invalid read of size 4
==19430==    at 0x40051C: main (x1.c:12)
==19430==  Address 0x4c18048 is 8 bytes inside a block of size 9 alloc'd
==19430==    at 0x4A0610C: malloc (vg_replace_malloc.c:195)
==19430==    by 0x4004E9: main (x1.c:8)
==19430==
1, 2147483647, 2147483647, 1025
==19430==
==19430== HEAP SUMMARY:
==19430==     in use at exit: 9 bytes in 1 blocks
==19430==   total heap usage: 1 allocs, 0 frees, 9 bytes allocated
==19430==
==19430== LEAK SUMMARY:
==19430==    definitely lost: 9 bytes in 1 blocks
==19430==    indirectly lost: 0 bytes in 0 blocks
==19430==      possibly lost: 0 bytes in 0 blocks
==19430==    still reachable: 0 bytes in 0 blocks
==19430==         suppressed: 0 bytes in 0 blocks
==19430== Rerun with --leak-check=full to see details of leaked memory
==19430==
==19430== For counts of detected and suppressed errors, rerun with: -v
==19430== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 4 from 4)

您可以从此输出中看到,您正在读取和写入已分配缓冲区的末尾。

【讨论】:

  • 感谢演示!
【解决方案4】:

在 C 语言中,指针可以指向它们想要的任何内存。在您的代码中,您只分配了 9 个字节。由于 C 语言不提供边界检查,您可以简单地将指针移动到任何位置。但是,这并不意味着您可以控制这些内存位置。它可能导致sigsegv、应用程序崩溃或AccessViolationException(如果此代码被其他一些语言(如 C# 或 Java)本机使用)。此外,这些字节可能会被其他可能破坏您的数据的程序修改。

【讨论】:

    猜你喜欢
    • 2010-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多