long 与 long long
当 int 的 32 位范围不足以容纳数据时,C 语言提供了 long 和 long long 两种更大的整数类型。long long 是 C99 新增的类型,保证至少 64 位,能存储约 ±922 亿的亿(即 ±9×10¹⁸)的整数。
long 类型
long(或 long int)标准保证至少 32 位。在 32 位 Linux 系统上通常是 32 位,在 64 位 Linux 系统上是 64 位;Windows 上始终是 32 位(LLP64 模型)。
long population = 7800000000L; /* 世界人口约 78 亿 */
printf("sizeof(long) = %zu\n", sizeof(long));
/* Linux 64 位:8;Windows 64 位:4;Linux 32 位:4 */
long 的取值范围:
#include <limits.h>
printf("LONG_MIN = %ld\n", LONG_MIN);
printf("LONG_MAX = %ld\n", LONG_MAX);
/* 32 位 long:-2147483648 到 2147483647 */
/* 64 位 long:-9223372036854775808 到 9223372036854775807 */
long 的大小不一致是 C 语言可移植性的一个痛点。如果代码在 Windows 和 Linux 之间移植,依赖 long 为 64 位的假设会出错。
long long 类型
C99 引入 long long(或 long long int),保证至少 64 位,在所有主流平台上都是 64 位。
long long universe_age = 13800000000LL; /* 宇宙年龄,约 138 亿年 */
printf("sizeof(long long) = %zu\n", sizeof(long long)); /* 通常是 8 */
long long 的取值范围:
#include <limits.h>
printf("LLONG_MIN = %lld\n", LLONG_MIN); /* -9223372036854775808 */
printf("LLONG_MAX = %lld\n", LLONG_MAX); /* 9223372036854775807 */
printf("ULLONG_MAX = %llu\n", ULLONG_MAX); /* 18446744073709551615 */
64 位 long long 能表示的最大值约为 1.8×10¹⁹,足以应对绝大多数场景:
/* 计算 50 的阶乘会远超 64 位范围 */
/* 但 20! = 2432902008176640000 可以放入 long long */
long long factorial(int n)
{
long long result = 1;
for (int i = 2; i <= n; i++)
result *= i;
return result;
}
printf("20! = %lld\n", factorial(20)); /* 2432902008176640000 */
无符号版本
unsigned long 和 unsigned long long 只表示非负数,正数范围扩大一倍:
unsigned long long huge = 18446744073709551615ULL;
printf("%llu\n", huge);
无符号类型常用于:
- 位运算和标志掩码
- 内存大小和数组索引(
sizeof返回size_t,通常是无符号类型) - 哈希值、校验和等不可能为负的数值
格式说明符
printf 和 scanf 的格式说明符必须与类型匹配:
| 类型 | printf | scanf |
|---|---|---|
long | %ld | %ld |
unsigned long | %lu | %lu |
long long | %lld | %lld |
unsigned long long | %llu | %llu |
long l = 1000000L;
long long ll = 1000000000000LL;
printf("long: %ld\n", l);
printf("long long: %lld\n", ll);
/* 读取 */
scanf("%lld", &ll);
C99 的 <inttypes.h> 提供了跨平台的格式宏,在类型大小不确定时更安全:
#include <inttypes.h>
int64_t value = 1234567890123LL;
printf("Value: %" PRId64 "\n", value); /* 展开为平台正确的格式 */
整数常量后缀
100 /* int */
100L /* long */
100UL /* unsigned long */
100LL /* long long */
100ULL /* unsigned long long */
后缀大小写均可(L/l、LL/ll、U/u),但建议用大写 L,因为小写 l 容易与数字 1 混淆。
跨平台整数类型
如果代码需要在不同平台间移植,<stdint.h> 的定宽类型是最佳选择:
#include <stdint.h>
int32_t exact32; /* 保证 32 位有符号 */
uint32_t uexact32; /* 保证 32 位无符号 */
int64_t exact64; /* 保证 64 位有符号 */
uint64_t uexact64; /* 保证 64 位无符号 */
这些类型在所有 C99 兼容平台上大小一致,消除了 int、long、long long 大小不确定的问题。
常见错误
格式说明符不匹配:
long long x = 10000000000LL;
printf("%d\n", x); /* 错误:%d 对应 int,x 是 long long */
printf("%lld\n", x); /* 正确 */
常量后缀遗漏:
long long big = 3000000000; /* 警告:3000000000 可能超出 int 范围 */
/* 在 32 位 int 平台上,这是未定义行为 */
long long correct = 3000000000LL; /* 正确:明确指定为 long long 常量 */
有符号与无符号混合:
long long s = -5;
unsigned long long u = 10;
if (s < u)
printf("s < u\n"); /* 不会输出:s 被转换为很大的无符号数 */