可以使用calloc(x, y)
安全地分配x
C中大小为y
的元素,calloc()
将负责乘法x*y
。
但是realloc()
例如仅将新大小作为参数,我想知道如何使用realloc()
安全地重新分配x*y
字节。
如果x*y
不适合size_t
怎么办?calloc()
如何处理这个问题?
size_t
是无符号类型,最大值size_t
是可以用realloc
或malloc
分配的对象的绝对最大大小;这在宏SIZE_MAX
中可用。在 32 位个人计算机上,size_t
通常是 32 位;64 位计算机上的 64 位。应该够了。
为了确保item_size * n_items
的计算不会溢出,您可以将SIZE_MAX
除以item_size
并确保结果值大于或等于n_items
:
size_t max_items = SIZE_MAX / item_size;
if (max_items < n_items) {
// an overflow would occur
}
else {
// it is ok
}
如果分配不成功,calloc
必须返回NULL
,因此calloc
很可能有一个类似于上面的检查。
简短的回答是你不能安全地做到这一点。 您最多可以做的是限制您尝试分配的内存量,使其不超过SIZE_MAX
。
SIZE_MAX
是sizeof
运算符可以产生的最大值。
C 语言中的每种数据类型都必须有一个可以使用sizeof
计算的大小,包括数组,以及使用malloc()
、calloc()
或realloc()
分配的任何连续内存块。
如果x*y
在数学上大于SIZE_MAX
,那么无论如何都不可能分配该内存量。 即使底层系统支持这一点,C 程序也无法完全使用该内存块。
还有一个问题是,计算x*y
(假设x
和y
是size_t
类型)将使用模算术,因此实际上会在数学上给出等同于(x*y)%(SIZE_MAX + 1)
的结果。
如果 x*y 不适合size_t怎么办?
calloc()
如何处理这个问题?
realloc()
和malloc()
受到限制,因为传递给它们的大小参数仅限于SIZE_MAX
。calloc()
不是这样
不需要兼容的 C 实现来限制calloc()
仅分配SIZE_MAX
的内存。 以下方法可能有效。 单个类型的最大大小可以SIZE_MAX
数组大小可以大"字节",但下面的SIZE_MAX
SIZE_MAX-1iptr
不是数组,而是指针。
// Assume sizeof(double) == 8
double *iptr = calloc(SIZE_MAX, sizeof *iptr);
重新分配如此大的指针是有问题的,因为它需要使用另一个调用来calloc()
如何避免
realloc
溢出?
OP 的问题不在于realloc()
可以处理什么,而在于代码如何计算传递给它的值可能会溢出。
要确保无符号类型(如size_t
)不会溢出乘法,请执行以下操作:
if (b && a > SIZE_MAX/b) Handle_Overflow();
prod = a*b;
size_t
是无符号类型。
对于无符号类型,溢出行为是确定性的,如C11
中所述
[....] 因为不能用生成的无符号整数类型表示的结果是 降模 大于最大值 1 的数字 由结果类型表示。
因此,只要确保生成要存储在变量类型中的值的操作不会在过程中溢出size_t
,将该变量传递给realloc()
就没有影响。最多,realloc()
将无法分配该内存。
如果x * y
溢出size_t
,realloc将尝试分配溢出的值:
bytes = (x * y) % 2^(sizeof(size_t) * 8)
因此,realloc
将"看到"bytes
字节数。它不照顾任何事情。