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 下的程序希望能抓住破坏堆数据结构的真正坏人......在调用此克隆函数之前发生的事情。
我想如果你已经检查了 width
和 height
是否合理(这样你就不会溢出),最好的做法是尝试使用 valgrid。这样你就可以看到你之前是否有一些内存错误可能会导致 malloc
行为不当,或者你是否有一个不够大的内存块。
你没有说你的目标平台是什么,但是这个:
int s = width * height;
如果 width * height
会产生大于 MAX_INT
的数字, 将导致溢出。 C标准只需要一个带符号的int
来存储最多+32767.
您的目标平台可能使用更大的整数,特别是如果它是桌面OS,但这仍然是不好的做法。
此外,您的函数签名允许将负值作为宽度或高度传递,但您的代码无法处理这种可能性。
编辑:总而言之,使用更合适的类型。 width
和 height
应该 可能 是 unsigned int
。如果是这样,那么 s
和 i
应该是 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。
有人可以帮助我理解以下行为:
我有一段用于克隆浮点图像的代码。
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 下的程序希望能抓住破坏堆数据结构的真正坏人......在调用此克隆函数之前发生的事情。
我想如果你已经检查了 width
和 height
是否合理(这样你就不会溢出),最好的做法是尝试使用 valgrid。这样你就可以看到你之前是否有一些内存错误可能会导致 malloc
行为不当,或者你是否有一个不够大的内存块。
你没有说你的目标平台是什么,但是这个:
int s = width * height;
如果 width * height
会产生大于 MAX_INT
的数字, 将导致溢出。 C标准只需要一个带符号的int
来存储最多+32767.
您的目标平台可能使用更大的整数,特别是如果它是桌面OS,但这仍然是不好的做法。
此外,您的函数签名允许将负值作为宽度或高度传递,但您的代码无法处理这种可能性。
编辑:总而言之,使用更合适的类型。 width
和 height
应该 可能 是 unsigned int
。如果是这样,那么 s
和 i
应该是 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。