reallocarray函数用法及注意事项

reallocarray 是某些类 Unix 系统(如 OpenBSD、FreeBSD)提供的一个内存分配函数,用于替代 realloc,主要目的是在动态分配内存时避免因整数溢出导致的安全漏洞。它特别适用于需要分配多个元素的内存场景(例如数组扩容),并自动检查元素数量与大小的乘积是否溢出。


函数原型

1
2
3
#include <stdlib.h> // 某些系统可能需要特定头文件

void *reallocarray(void *ptr, size_t nmemb, size_t size);
  • 参数
    • ptr: 已分配内存的指针(可以是 NULL)。
    • nmemb: 需要分配的元素数量。
    • size: 每个元素的大小。
  • 返回值
    • 成功时返回新内存的指针,失败返回 NULL,且原内存块保持不变。

核心功能

  1. 重新分配内存:类似 realloc,可以扩大或缩小内存块。
  2. 溢出检查:自动检查 nmemb * size 是否溢出。若溢出,直接返回 NULL 并设置 errnoENOMEM

典型用法示例

1
2
3
4
5
6
7
8
9
10
11
int *arr = malloc(10 * sizeof(int)); // 初始分配
size_t new_count = 20;

// 使用 reallocarray 调整内存大小
int *tmp = reallocarray(arr, new_count, sizeof(int));
if (tmp == NULL) {
// 处理错误:旧内存 arr 仍有效
free(arr);
return -1;
}
arr = tmp; // 更新指针

注意事项

  1. 可移植性

    • reallocarray 并非 C 标准库函数,仅在部分系统(如 OpenBSD、FreeBSD)中可用。
    • 在 Linux 中需定义 _GNU_SOURCE 宏(通过 #define _GNU_SOURCE)并包含 <stdlib.h>
    • Windows 或其他平台可能需要手动实现或替换为 realloc + 溢出检查。
  2. 错误处理

    • 必须检查返回值:若返回 NULL,原内存块(ptr)仍有效,需手动决定是否释放。
    • ptrNULL,则行为类似 malloc(nmemb * size),但仍检查溢出。
  3. realloc 的区别

    • realloc 的调用形式为 realloc(ptr, nmemb * size),但不会检查乘法溢出。
    • reallocarray 显式分离 nmembsize,并在计算总大小时检查溢出,更安全。
  4. 释放内存

    • 使用 free 释放由 reallocarray 分配的内存,与 malloc/realloc 一致。
  5. 旧指针处理

    • reallocarray 失败,原指针 ptr 仍指向有效内存,需根据业务逻辑决定是否继续使用或释放。

替代方案(不可用时)

如果系统不支持 reallocarray,可手动实现类似逻辑:

1
2
3
4
5
6
7
8
9
#include <errno.h>

void *safe_reallocarray(void *ptr, size_t nmemb, size_t size) {
if (size != 0 && nmemb > SIZE_MAX / size) {
errno = ENOMEM;
return NULL;
}
return realloc(ptr, nmemb * size);
}

应用场景

  • 动态数组扩容(如 vector 实现)。
  • 处理用户输入或不可信数据时分配内存,避免溢出攻击。
  • 需要高安全性的内存分配操作。

使用时需结合系统环境权衡可移植性与安全性。