//gcc 5.4.0
#include <stdio.h>
int main(void)
{
// Try to reserve 1 TB of memory
int *arr = malloc(1024 * 1024 * 1024 * 1024);
// Unfortunately most systems will actually allow this (swapping etc.)
if (arr == NULL) {
printf("Was not able to reserve memory!n");
}
printf("Everything is okn");
return 0;
}
编译器选项:-Wall -std=c99 -o a.out source_file.c
输出("一切都很好")让我感到困惑。我显然没有 1TB 的内存 - 根据 C 参考,malloc 应该返回 NULL,如果且仅当(!)出现问题。
这显然不会在这里发生。有人能解释一下吗?
* 1024 * 1024
这些是int
类型的整数常量。当相乘时,操作将在类型int
上执行。结果的类型为int
。
int
很可能无法在系统上保存大于 2^32/2 -1 =2.14*10^9
的值。
这意味着您溢出有符号的 int 并调用未定义的行为。任何事情都可能发生,包括malloc分配一些随机的数据块。
根据 C-Reference,malloc 应该返回 NULL,当且仅当(!)出现问题时。
这是不正确的。
malloc()
在 2 种情况下返回NULL
:
-
如果无法分配内存,则为空指针。
-
在分配 0 时,如果
malloc()
返回NULL
或非NULL
指针(不能取消引用),则定义实现。
OP 的1024 * 1024 * 1024 * 1024
溢出 32int
位数学,这是未定义的行为或 UB。 @WhozCraig提示。典型的结果是截断的乘积为 0。然后,OP 的系统返回一个非NULL
指针用于零字节分配,因为未打印"无法保留内存!"。
如果OP的平台使用罕见的64位int
,内存的真正分配可能会延迟。
OP也错过了编译器的有用建议。 增加警告选项。 某些以下人员将报告警告。
// -pedantic -Wall -Wextra -std=c99 -o a.out source_file.c
int *arr = malloc(1024 * 1024 * 1024 * 1024);
// warning: integer overflow in expression [-Woverflow]
确保表达式的数学类型满足/超过目标的类型。 不使用
100*1000*1000@Olaf原因int *arr = malloc((size_t) 1024 * 1024 * 1024 * 1024);
从malloc(3)
手册页:
默认情况下,Linux 遵循乐观的内存分配策略。这意味着当 malloc() 返回非 NULL 时,不能保证内存确实可用。
当然,这是假设你在Linux上。
您没有 1TB 的物理内存,但大多数操作系统都支持虚拟内存,由磁盘空间提供支持。 当 malloc 请求内存时,系统会分配虚拟内存。其中一部分将是真实内存,其余部分很可能是高清上的文件。
1024 * 1024 * 1024 * 1024
最有可能调用未定义的行为:1024
是一个int
常数。操作是使用两个操作数的"最大"类型完成的,但至少int
.两个操作数都是int
,所以乘法类型是int * int -> int
。以下乘法也是如此。即最终结果是int
.在典型系统上int
不超过 32 位。参数的类型与表达式无关。
乘法的结果溢出一个int
(除非它超过 41 位),这会调用未定义的行为。任何事情都可能发生(通常,但不能保证:结果被截断,产生0
)。
为避免这种情况,您应该使用(size_t)1024 * 1024 * 1024 *1024
.这将(根据转换规则)使用系统上size_t
具有的任何标准类型执行计算。
事先使用静态断言来确保size_t
可以表示结果:
_Static_assert(SIZE_MAX >= 1024ULL * 1024 * 1024 *1024, "size_t is too small");`
没有这个,乘法将在太小的size_t
上被截断(有符号和无符号整数的规则不同!),你最终会传递0
。
根据标准malloc(0)
是定义NULL
或指针的实现,不得用于访问对象。
请记住,您的代码可能会返回一个非 null 指针,该指针不得用于访问。但是当你只是测试它时,它很好,但没用。