【问题标题】:Access violation after mallocmalloc 后访问冲突
【发布时间】:2015-10-28 14:22:59
【问题描述】:

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

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

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

malloc 怎么可能返回一个错误的指针?

代码:

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;
}

非常感谢任何帮助。

更新:

平台:Windows 64 位

崩溃时变量的值:

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

【问题讨论】:

  • 从不投射malloc的结果
  • 很可能,在您的程序中的某个地方存在导致堆损坏的东西,进而破坏了使用堆的程序的其他部分。
  • 但是FFS,请停止抱怨关于强制转换malloc。我敢肯定,每个该死的 C 问题的评论部分都有更重要的事情要讨论。
  • @Chris Dead horse malloc cast debate can be found here。现在,请大家不要再用这些无关紧要的、离题的言论向这个问题发送垃圾邮件了吗?
  • @KeylorSanchez:实际上,如果我们使用全语言 C 结构赋值是正确的做法。 memcpy 对整个结构进行平面的、无效的复制,但根据 C 语言标准,实际上只定义了对实际结构元素的访问。根据编译器填充结构的方式,memcpy 还将在元素“之间”读/写,也就是说,如果我们是超技术且完全强迫症,则非法和未定义的行为。

标签: c malloc access-violation


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    这个

    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;
    }
    

    【讨论】:

    • 减号。 认真?你认为这张图片有多少百万像素?
    • @KarolyHorvath: (4*1024*1024*1024/(sizeof(float)*4))**0.5 = 16384 - 这不是那么多,说实话。我经常使用 4 倍宽和高的图像。 – 也可能 OP 将负值传递给宽度或高度,这会导致同样的问题。
    • 其实这个问题应该不会导致这个错误。首先溢出不是未定义的行为,它实际上是明确定义的行为(IIRC)。如果你得到错误的 s 值,你仍然会得到 i&lt;s 。唯一有风险的是s*sizeof(Color),它可能由于溢出而分配了一个太小的缓冲区(但是你必须遇到数组无论如何都不适合可寻址内存的情况)
    • 好的,所以它是未定义的,但实际上你在这里使用乘法指令,并且在大多数情况下,结果应该是处理器提供的任何结果,或者处理器在那里引发异常。跨度>
    • @KarolyHorvath:由于 OP 现在给出了变量的实际值,是的,堆损坏似乎是罪魁祸首。尽管如此,我的回答仍然很重要(恕我直言),所以我认为更改或删除它没有意义。 OP 可能确实存在堆损坏。其他有类似问题的人,这是由于无效值,但是可以搜索这个,然后讨论溢出行为和检查是必要的阅读材料。
    【解决方案3】:

    我想如果你有范围检查 widthheight 是合理的(所以你不会溢出)最好的做法是尝试使用 valgrid。这样,您将能够查看在此之前是否存在可能导致 malloc 行为不端的内存错误,或者您实际上是否有一个不够大的内存块。

    【讨论】:

      【解决方案4】:

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

      int s = width * height;
      

      如果width * height 产生的数字大于MAX_INT,将导致溢出。 C 标准只需要一个签名的int 来存储最多 +32767。

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

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

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

      【讨论】:

      • 更多尝试澄清@datenwolf 答案的一个方面并解释为什么这是一个坏主意。我认为最好从一开始就使用更合适的类型,而不是尝试检测溢出。
      • 注意:OP 已添加目标平台是 Win64,因此 int 可能足够宽,不会导致溢出。虽然仍然是不好的做法,而且不是跨平台的。
      • “我认为最好从一开始就使用更合适的类型,而不是尝试检测溢出。” - 好点,但你没有在答案中提出建议。 ;-) 如果您这样做,使用标准类型,而不是特定于 Windows 的类型。有孩子在看。 ;-)
      • 好的,谢谢@DevSolar,我已经编辑了答案以使这一点更清楚。
      • "应该是" size_t.
      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

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

        Visual Studio 打印:

        警告 C4013:'malloc' 未定义;假设 extern 返回 int

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2023-03-26
          • 1970-01-01
          • 1970-01-01
          • 2016-06-28
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多