【问题标题】:Heap corruption from memory allocation using malloc: why did it happen?使用 malloc 分配内存造成的堆损坏:为什么会发生?
【发布时间】:2012-10-07 17:50:31
【问题描述】:

好的,我试图实现 memmove 只是作为一个编程练习,当我尝试使用 malloc 时,我在 memmove 函数中遇到内存访问冲突。这是函数:

//Start

void* MYmemmove (void* destination, const void* source, size_t num) { 

    int* midbuf = (int *) malloc(num); // This is where the access violation happens.
    int* refdes = (int *) destination; // A pointer to destination, except it is casted to int*
    int* refsrc = (int *) source; // Same, except with source
    for (int i = 0;num >= i;i++) { 
        midbuf[i] = *(refsrc + i); // Copy source to midbuf
    }
    for (int i = 0;num >= i;i++) { 
        refdes[i] = *(midbuf + i); // Copy midbuf to destination
    } 
    free(midbuf); // free midbuf 
    refdes = NULL; // Make refdes not point to destination anymore
    refsrc = NULL; // Make refsrc not point to source anymore
    return destination;
}

顺便说一句,我是指针的新手,所以如果有一些错误不要感到惊讶。 我做错了什么?

【问题讨论】:

  • 您不需要将 malloc 的返回值或实际上任何 void 指针转换为任何其他指针类型。仍在查看您的代码,仅供参考。
  • @Dan:C++ 不是这样,问题也有 C++ 标签。
  • memmove 占用多个字节,但您使用的是整数。
  • memmove 的标准库实现不使用中间缓冲区。另外,最后将refdesrefsrc设置为NULL是没有意义的:函数即将返回,因此无法使用。
  • 除了malloc的大小问题,for循环中的比较是错误的;它应该是num > i,而不是num >= i。就风格而言,i < num 是常用的写法。

标签: c++ c pointers memmove


【解决方案1】:

请注意其他建议!答案取决于您的 memmove 将如何使用。其他答案表明您应该更改 malloc 调用以考虑 int 的大小。但是,如果您的 memmove 函数将用于表示“移动此数量的 字节”,那么实现将是错误的。我会改为使用 char*,因为它可以一次性解决几个问题。

另外,int 通常为 4 个字节,char 通常为 1 个字节。如果您收到的void* 地址不是字对齐的(不是 4 字节的倍数),您将遇到问题:要复制非字对齐的 int,您将不得不进行多次读取和昂贵的位掩码。这是低效的。

最后,内存访问冲突发生了,因为您每次都在增加 midbuf int 指针,并且每次向前移动 4 个字节。但是,您只分配了 num bytes,因此最终会尝试访问超出分配区域的末尾。

/** Moves num bytes(!) from source to destination */
void* MYmemmove (void* destination, const void* source, size_t num) { 

    // transfer buffer to account for memory aliasing
    // http://en.wikipedia.org/wiki/Aliasing_%28computing%29
    char * midbuf = (char *) malloc(num); // malloc allocates in bytes(!)
    char * refdes = (char *) destination;
    char * refsrc = (char *) source;

    for (int i = 0; i < num; i++) { 
        midbuf[i] = *(refsrc + i); // Copy source to midbuf
    }

    for (int i = 0; i < num; i++) { 
        refdes[i] = *(midbuf + i); // Copy midbuf to destination
    } 

    free(midbuf); // free midbuf
    // no need to set the pointers to NULL here.
    return destination;
}

通过逐字节复制,我们避免了对齐问题,以及 num 本身可能不是 4 个字节的倍数的情况(例如 3,因此 int 对于该移动来说太大了)。

【讨论】:

  • 谢谢,我看到人们出于类似的原因在内存读写中使用 char;只是不太确定要使用它。感谢您的澄清。 :)
  • 哦,顺便说一句,由于这些建议,我的功能现在可以工作了。谢谢你们!
【解决方案2】:

用 malloc 替换字符串:

int* midbuf = (int *) malloc(num*sizeof(int)); 

问题是您分配的不是num int 元素,而是num 字节。

【讨论】:

  • 如果是 c 转换 malloc 的返回值是个坏主意。可以隐藏分配失败
  • @fayyazkl:呃,怎么样?该值仍为 null。
  • 嗯,是的,这修复了内部缓冲区溢出。但是代码仍然复制了比预期更多的字节。原来的malloc 调用是正确的;这是错误的指针类型。
  • @GManNickG 如果 malloc 的原型不正确(如果您在嵌入式平台上构建并同时编译库,这通常是可能的),转换返回值不会解决任何问题。 malloc() 仍将返回 int,并且将该 int 转换为对象指针类型不会神奇地将其更改为指针。强制转换 malloc() 将使编译器认为它没有损坏,尽管它确实是在返回 int 时。
  • @fayyazkl - 这取决于。 如果一个 int 和一个指针的大小相同,则强制转换工作正常。这是函数原型出现之前的旧 C 语言遗产。如今,在某些系统上,int 和指针的大小不同,并且正如您所说,强制转换确实隐藏了问题。
【解决方案3】:
int* midbuf = (int *) malloc(num); // This is where the access violation happens.
int* refdes = (int *) destination; // A pointer to destination, except it is casted to int*
int* refsrc = (int *) source; // Same, except with source
for (int i = 0;num >= i;i++) { 
    midbuf[i] = *(refsrc + i); // Copy source to midbuf
}

mallocnum 字节,但在循环中,您尝试复制 num ints。由于int 通常占用超过一个字节,因此您的访问超出了范围。

【讨论】:

    【解决方案4】:

    内存访问冲突?

    您正在尝试访问您无权访问的内存。也许你有一个空指针或者指针指向另一个程序或代码段。

    【讨论】:

    • 如果你有一个指向 0x00000000 的指针并且你访问它并且它给你一个内存访问冲突,那么这就是指针和它指向的位置......特别是。请注意,它不必是 0x00000000,它可以是程序之外的任何地址。
    【解决方案5】:

    问题是您将num 字节分配到midbuf,然后将num 整数复制到其中。由于int 在大多数平台上大于一个字节,因此您遇到了问题。将您的malloc 更改为num*sizeof(int),您将不会遇到这个问题。

    【讨论】:

    • -1 - memmove 的第三个参数指定要移动的 字节数malloc 是正确的;指针类型错误。
    • 如上 - 答案是否正确,取决于您对问题的理解。
    • 好吧,只有当 MYmemmove 与 memmove 相同时。无论如何,他有问题。
    • @Dan - “我试图将 memmove 实现为编程练习”。
    【解决方案6】:

    有两个问题需要看

    1.内存空间

    (前提是MYmemmove 执行自定义实现以移动ints 如问题所示)

        int* midbuf = (int *) malloc(num * sizeof(int));
    

    malloc 是基于字节的,将分配 num 个字节。 int * 是指向 ints 的指针。意思是midbuf[x] 将从midbuf + sizeof(int)*x 访问内存。您想分配 num ints (int 的大小取决于体系结构,通常为 4 或 2 个字节)。因此malloc(num * sizeof(int))

    2。数组索引

        for (int i = 0;num > i;i++) { 
    

    在 C(和 C++)中,数组是从 0 开始的,即第一个索引是 0。你做对了。但这也意味着如果您保留 num ints,则可用索引将从 0num-1。在您的循环中,i 将在 0num 之间变化,这要归功于条件 num &gt;= i,这意味着您将访问 num+1 项目。所以num &gt; i(或i &lt; num)会是更好的for条件。

    【讨论】:

    • -1 - memmove 的第三个参数表示要移动多少 字节malloc 调用是正确的;这是错误的指针类型。
    • 实际的memmove 函数移动字节。从最初的问题中不清楚MYmemmove 不是移动ints 的自定义版本。我编辑了我的答案以考虑到这一点。
    • 看问题时上面的答案是正确的:为什么会发生内存冲突?当谈到“什么是实现 mmove 的正确方法”时,Pete 是正确的。
    • @ring0 - 很清楚:“我试图将 memmove 实现为编程练习”。
    【解决方案7】:
    #include <stdlib.h>  // did you included this?
    
    
    void* MYmemmove (void* destination, const void* source, size_t num) { 
    
        char *Source = source, *Destination = destination;
        char *Middle = malloc( sizeof(char) * num );    // midbuf
    
        for (int i = 0; i < num ; i++) { 
            Middle[i] = Destination[i]; // Copy source to midbuf
        }
        for (int i = 0; i < num ; i++) { 
            Destination[i] = Middle[i]; // Copy midbuf to destination
        }
    
        free(Middle);                   // free memory allocated previously with malloc
        return destination;
    }
    

    可能会发生访问冲突,因为您没有包含 malloc 所需的库(如果您忘记了函数定义,标准 c 不会在您的脸上抛出错误)。您不需要将指针标记为 NULL,因为 C 中没有垃圾收集(指针就是指针。指向内存中某个点的地址,而不是内存本身)。

    想想像这样的指针

    指针地址 0x1243 0x4221 目的地 -> {某种数据}

    目的地 = 0x1243 *Destination = 当前地址 0x4221 处的任何值

    您也不能索引 void 指针。您必须首先将它们转换为某种类型,以便编译器知道它们需要多少偏移量。

    目的地[x] = *(目的地+x)

    char 是 1 个字节,所以 char 指针实际上会移动 x 个字节,但 int 是 4 个字节,而 int 指针会移动 4*x 个字节。如果这听起来很技术性,请不要太担心,当你达到非常低的水平时,它会很重要;)

    【讨论】:

    • -1 memmove 即使两个数组重叠也可以工作;在这种情况下,此实现将产生废话。
    猜你喜欢
    • 2019-10-23
    • 2019-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多