如何避免 realloc 溢出?

How to avoid overflow in realloc?

可以使用 calloc(x, y) 在 C 中安全地分配 x 个大小为 y 的元素,而 calloc() 将负责乘法 x*y

但是 realloc() 例如仅将新大小作为参数,我想知道如何使用 realloc().

安全地重新分配 x*y 字节

如果 x*y 不适合 size_t 怎么办? calloc() 如何处理这个问题?

size_t 是无符号类型。

对于无符号类型,溢出行为是确定性的,如 C11

中所述

[....] because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.

因此,只要您确保生成要存储在变量类型 size_t 中的值的操作不会在过程中溢出,将该变量传递给 realloc() 就没有影响.最多,realloc() 将无法分配该内存。

如果 x * y 溢出 size_t,realloc 将尝试分配溢出的值:

bytes = (x * y) % 2^(sizeof(size_t) * 8)

因此 realloc 将 "see" bytes 字节数。它不关心任何事情。

size_t是无符号类型,size_t的最大值是reallocmalloc可以分配的对象的绝对最大大小;这在宏 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 必须 return NULL 如果分配没有成功,所以 calloc 很可能有一个类似于上面的检查。

简短的回答是你不能安全地做到这一点。您最多可以做的是限制您尝试分配的内存量,使其不超过 SIZE_MAX.

SIZE_MAXsizeof 运算符可以产生的最大值。

C 中的每个数据类型都必须具有可以使用 sizeof 计算的大小,包括数组,以及使用 malloc()calloc() 或 [分配的任何连续内存块=16=].

如果 x*y 在数学上大于 SIZE_MAX,则不可能以任何方式分配该数量的内存。即使底层系统支持,C 程序也无法完全使用该内存块。

还有一个问题是计算 x*y(假设 xysize_t 类型)将使用模运算,因此实际上会给出结果数学上等同于 (x*y)%(SIZE_MAX + 1).

What if x*y doesn't fit in size_t? How does calloc() handle this?

realloc()malloc() 的限制在于传递给它们的大小参数被限制为 SIZE_MAXcalloc()

不是这样

兼容的 C 实现 不需要 来限制 calloc() 仅分配 SIZE_MAX 的内存。以下可能有效。单个类型的最大大小可以为 SIZE_MAX,而 array 的大小可以达到 SIZE_MAX SIZE_MAX-1"bytes",但下面的iptr不是数组,而是指针。

// Assume sizeof(double) == 8
double *iptr = calloc(SIZE_MAX, sizeof *iptr);

重新分配如此大的指针是有问题的,因为它需要再次调用 calloc()


How to avoid overflow in realloc?

OP 的问题与其说是 realloc() 可以处理的问题,不如说是代码如何计算传递给它的值可能会溢出。

为了确保 unsigned 类型,如 size_t,不会溢出乘法:

  if (b && a > SIZE_MAX/b) Handle_Overflow();
  prod = a*b;