malloc 后访问冲突

Access violation after malloc

有人可以帮助我理解以下行为:

我有一段用于克隆浮点图像的代码。 Clone 方法将指向另一个图像的指针及其尺寸作为参数。

一切都按预期工作,但有时这一行 clone[i] = color; 会导致访问冲突。异常的发生是不可预测的,也不是周期性的。在崩溃时检查变量表明 Color color = source[i]; 始终设置且有效。

malloc returns 怎么可能是一个错误的指针?

代码:

typedef struct
{
    float r;
    float g;
    float b;
    float a;
} Color;

Color* Clone(Color* source, int width, int height)
{
    int s = width * height;

    Color *clone;
    clone = (Color *)malloc(s * sizeof(Color));

    if (clone)
    {
        for (int i = 0; i < s; i++)
        {
            Color color = source[i];

            // Sometimes app crash here: Access violation
            clone[i] = color;
        }
    }

    return clone;
}

非常感谢任何帮助。

更新:

平台:Windows64位

崩溃时的变量值:

width = 256
height = 256
s = 655536
i = 0

这个

int s = width * height;

容易出现乘法整数溢出。如果发生这种情况,您将调用未定义的行为(因为未定义带符号整数的溢出行为);通常 malloc 会分配太短的缓冲区。

编辑:如果宽度或高度中的任何一个为负数,也会发生这种未定义的溢出。

为避免这种情况,您必须检查乘法溢出。唯一可靠的方法是使用无符号算术(定义了溢出行为)。

if( 0 > width
 || 0 > height
){
    return ERROR_INVALID_VALUE;
}
size_t const sz_width  = width;
size_t const sz_height = height;
/* ((size_t)x) != x makes use of arithmetic conversion
 * rules to check for truncation by the cast  */
if( sz_width  != width
 || sz_height != height
){
    return ERROR_TRUNCATION;
}

/* now check if the multiplication overflows */
/* size_t is unsigned, so overflow is well behaved */
size_t const sz = sz_width * sz_height;
if( (sz / sz_width) != sz_height ) {
    return ERROR_OVERFLOW;
}

我看不出这段代码有什么大错。但是,如果堆之前已损坏,malloc 确实可以 return 垃圾。实际上 malloc 经常是当一个人检测到出现问题并且您收到明确的 "heap corruption" 错误消息时。

我的建议是,如果可能的话,运行 valgrind 下的程序希望能抓住破坏堆数据结构的真正坏人......在调用此克隆函数之前发生的事情。

我想如果你已经检查了 widthheight 是否合理(这样你就不会溢出),最好的做法是尝试使用 valgrid。这样你就可以看到你之前是否有一些内存错误可能会导致 malloc 行为不当,或者你是否有一个不够大的内存块。

你没有说你的目标平台是什么,但是这个:

int s = width * height;
如果 width * height 会产生大于 MAX_INT 的数字,

将导致溢出。 C标准只需要一个带符号的int来存储最多+32767.

您的目标平台可能使用更大的整数,特别是如果它是桌面OS,但这仍然是不好的做法。

此外,您的函数签名允许将负值作为宽度或高度传递,但您的代码无法处理这种可能性。

编辑:总而言之,使用更合适的类型。 widthheight 应该 可能 unsigned int。如果是这样,那么 si 应该是 unsigned long.

malloc 调用可能不是您的问题(正如@KarolyHorvath 所说,大小并不是很大)。最有可能的问题是传入的source为null或者为空;你应该在尝试引用 source[i].

之前检查一下

当您在 Visual Studio 中编译不包含 stdlib.h 的 .c 源代码时(其中 malloc 定义为返回 void*)并且 Visual Studio 使用其自己的定义,其中 malloc returns 整数。

Visual studio 打印:

warning C4013: 'malloc' undefined; assuming extern returning int

所以您的指针被截断为 4 个字节而不是 8 个字节。似乎只有在 .c 文件中以 x64 模式编译时才会出现此问题。

所以,解决方案是 - 只包含 stdlib.h。