C语言long存大数失败?unsigned long循环中异常归零问题
问题描述
我没法把4294967296及更大的数值存入long类型变量。循环第二次迭代时,尽管change是unsigned long类型,相乘后却变成0。我试过换成long long、long int等类型,也显式把所有算术操作转成unsigned long,但都没用。这个函数本来应该按字节交错两个整数(比如c245a1和1e67ba交错成c21e4567a1ba),但long和long long都存不下这么大的数。
原代码
unsigned long interleave(unsigned int i1, unsigned int i2) { long int out = 0l; unsigned long r1; unsigned long r2; unsigned long change; change = 1l; unsigned long store; unsigned long bitSize = 256l; printf("%010x + %010x = ", i1, i2); //printf("START CHANGE %016x or %lu\n", change, change); for (int i = 3; i >= 0; i--) { //Get remainder for int 1; r1 = (unsigned long)i1 % (unsigned long)bitSize; i1 = (unsigned long)i1 / (unsigned long)bitSize; //Get remainder for int 2; r2 = (unsigned long)i2 % (unsigned long)bitSize; i2 = (unsigned long)i2 / (unsigned long)bitSize; //printf("GOT %04x and %04x\n", r1, r2); store = ((unsigned long)r1 * (unsigned long)bitSize) + (unsigned long)r2; store = (unsigned long)store * (unsigned long)change; //printf("STORE %016x\n", store); change = ((unsigned long)change) * ((unsigned long)bitSize); printf("CHANGE AFTER FIRST MULTIPLICATION: %016x or %lu\n", change, change); change = ((unsigned long)change) * ((unsigned long)bitSize); printf("CHANGE AFTER SECOND MULTIPLICATION: %016x or %lu\n", change, change); //printf("OUT: %016x\n", out); out += (unsigned long)store; } printf("%016x", out); } int main() { interleave((unsigned)4294967295, (unsigned)2999999); char* ch; scanf("%c%c", ch, ch); }
问题分析与修复方案
核心问题
- 类型位宽不足:目标是生成8字节(64位)的交错结果,但
out被定义为long int,多数系统中long int是32位,根本存不下64位数值;就算用long long,若中间变量类型不匹配,依然会触发溢出。 - 无符号整数溢出回绕:
change每次循环要乘以256*256=65536,在32位unsigned long环境下,第二次迭代时change会达到2^32,超过32位无符号整数最大值(2^32-1),触发溢出回绕,结果直接变为0。 - main函数内存错误:
char* ch未初始化就传给scanf,会导致非法内存访问。
修复步骤
- 统一使用64位无符号类型:用
uint64_t(需包含<stdint.h>)或unsigned long long存储结果和中间变量,确保有足够位宽容纳8字节数据。 - 修正变量定义:把
out、change、store等全部改为64位无符号类型,避免类型转换导致的溢出。 - 调整打印格式符:打印64位无符号整数时,用
%llx(对应unsigned long long)或%lx(若unsigned long是64位),避免格式不匹配。 - 修复main函数输入逻辑:替换未初始化的指针,用
getchar()等待输入。
修复后代码
#include <stdio.h> #include <stdint.h> uint64_t interleave(unsigned int i1, unsigned int i2) { uint64_t out = 0; uint64_t r1, r2, change = 1; uint64_t store; const uint64_t bitSize = 256; printf("%08x + %08x = ", i1, i2); for (int i = 3; i >= 0; i--) { r1 = (uint64_t)i1 % bitSize; i1 /= bitSize; r2 = (uint64_t)i2 % bitSize; i2 /= bitSize; store = (r1 * bitSize + r2) * change; change *= bitSize * bitSize; out += store; } printf("%016llx\n", out); return out; } int main() { interleave((unsigned int)4294967295, (unsigned int)2999999); getchar(); return 0; }
额外说明
- 无符号整数溢出是C标准定义的合法行为(自动回绕),但这不是我们需要的逻辑,必须用足够位宽的类型避免溢出。
uint64_t比unsigned long long更具可移植性,它明确表示64位无符号整数,在所有标准编译器环境下都能保证位宽。
内容的提问来源于stack exchange,提问作者John Serlin
相关产品推荐
相关产品推荐

