哈,这是个经典问题。e.lund 已经说出了原因,我补充一下你那条 "out of memory" 是怎么来的。
如果编译器把 < 0 的判断删掉了,total 里仍然是回绕后的值 INT_MIN。然后 malloc(total) 会把它转成 size_t。在 64 位机器上这就变成 0xFFFFFFFF80000000,大约 16 EB。malloc 拒绝了,你的 OOM 分支就触发了。所以 malloc 没什么奇怪的,只是被要求分配一个离谱的大小。(顺带一提,这也是为什么“只要检查 NULL 就行”那派人在这里会有一种虚假的安全感。)
我的问题在声明上。count 和 rec_size 是什么类型?如果它们是从文件里按 uint32_t 读出来的,那乘法是按无符号算术做的,合法回绕。然后赋给 int total 是实现定义的行为,不是 UB。这样情况就大不一样了。
你试过在 -O2 下加 -fsanitize=undefined 吗?它应该能直接标出这个乘法。另外加上 -fwrapv 之后,两个构建的行为会重新一致吗?
从英语翻译·查看原文Ha, this is a classic. e.lund has the cause. I'll add the part that explains your "out of memory" message.
If the compiler drops the < 0 test, total still holds the wrapped value, INT_MIN. Then malloc(total) converts it to size_t. On a 64-bit box that becomes 0xFFFFFFFF80000000, which is about 16 exabytes. malloc says no, and your OOM branch fires. So malloc isn't odd. It's just being asked for something absurd. (Side note: that's also why the "just check for NULL" crowd gets a false sense of safety here.)
My question is about the declarations. What types are count and rec_size? If they're read from the file as uint32_t, the multiply happens in unsigned arithmetic and wraps legally. Then the int total assignment is implementation-defined, not UB. That would change the story a lot.
Have you tried -fsanitize=undefined at -O2? It should flag the multiply directly. And does -fwrapv make the two builds agree again?