calloc()总共可以分配超过SIZE_MAX吗?

Tob*_*ght 30 c language-lawyer

最近的一次代码审查中,有人声称

在选择系统上,calloc()可以分配超过SIZE_MAX总字节数,但是malloc()有限.

我的主张是错误的,因为calloc()为一个对象数组创建了空间 - 作为一个数组,它本身就是一个对象.没有任何物体的尺寸可以大于SIZE_MAX.

那么我们哪个是正确的?在地址空间大于范围的(可能是假设的)系统上size_t,calloc()当使用产品大于SIZE_MAX?的参数调用时,是否允许成功?

为了使它更具体:以下程序是否会以非零状态退出?

#include <stdint.h>
#include <stdlib.h>

int main()
{
     return calloc(SIZE_MAX, 2) != NULL;
}
Run Code Online (Sandbox Code Playgroud)

chu*_*ica 20

calloc()总共可以分配超过SIZE_MAX吗?

作为断言"在选择系统上,calloc()可以分配超过SIZE_MAX总字节数,但是malloc()有限." 来自我发布的评论,我将解释我的理由.


为size_t

size_t是一些至少16位的无符号类型.

size_t这是sizeof运算符结果的无符号整数类型; C11dr§7.192

" size_t SIZE_MAX其实施定义的值应等于或大于......下面给出的相应值"...... ...的限制为65535§7.20.32

的sizeof

sizeof操作者产生其操作数的大小(以字节为单位),其可以是表达或类型的括号名称.§6.5.3.42

释放calloc

void *calloc(size_t nmemb, size_t size);
Run Code Online (Sandbox Code Playgroud)

calloc函数为一个nmemb对象数组分配空间,每个对象size都是大小.§7.22.3.22


考虑一个nmemb * size远远超过的情况SIZE_MAX.

size_t alot = SIZE_MAX/2;
double *p = calloc(alot, sizeof *p); // assume `double` is 8 bytes.
Run Code Online (Sandbox Code Playgroud)

如果calloc()真正分配的nmemb * size字节,如果p != NULL是真的,这违反了什么规范?

每个元素的大小(每个对象)都是可表示的.

// Nicely reports the size of a pointer and an element.
printf("sizeof p:%zu, sizeof *p:%zu\n", sizeof p, sizeof *p); 
Run Code Online (Sandbox Code Playgroud)

可以访问每个元素.

// Nicely reports the value of an `element` and the address of the element
for (size_t i = 0; i<alot; i++) {
  printf("value a[%zu]:%g, address:%p\n", i, p[i], (void*) &p[i]); 
}
Run Code Online (Sandbox Code Playgroud)

calloc() 细节

" nmemb对象数组的空间":这当然是争论的关键点."为数组分配空间"是否需要<= SIZE_MAX?我在C规范中没有发现任何要求这个限制的内容,因此得出结论:

calloc()可能会分配超过SIZE_MAX总数.


使用大量参数返回不符合规定的情况肯定是不常见的.通常这种分配超出可用内存,因此问题没有实际意义.我遇到的唯一情况是巨大的内存模型,其中16位,对象指针是32位.calloc()NULLsize_t


Lun*_*din 17

SIZE_MAX没有必要指定对象的最大大小,而是指定最大值size_t,这不一定是同一个东西.请参阅为什么数组的最大大小"太大"?,

但很明显,传递一个更大的值而不是SIZE_MAX一个期望一个size_t参数的函数是没有明确定义的.所以理论上SIZE_MAX是限制,理论上calloc允许SIZE_MAX * SIZE_MAX分配字节.

malloc/的关键calloc是他们分配没有类型的对象.具有类型的对象具有限制,例如从不大于某个限制SIZE_MAX.但是这些函数的结果指出的数据没有类型.它还不是一个数组.

形式上,数据没有声明的类型,但是当您在分配的数据中存储某些内容时,它会获得用于存储的有效数据访问类型(C176.5§6).

这反过来意味着可以calloc分配比C中任何类型更多的内存,因为分配的内容不具有类型.

因此,就C标准而言,calloc(SIZE_MAX, 2)返回与NULL不同的值是完全正确的.如何以合理的方式实际使用分配的内存,或者甚至在堆上支持如此大块内存的系统是另一个故事.