unsigned long 是 C 语言中一种非常重要的基本数据类型,它用于存储非负整数(即零和正整数),下面我将从多个方面为你进行详细解释。

核心概念:unsigned 的含义
在 C 语言中,unsigned 关键字表示“无符号”,意思是这个类型的变量不能存储负数。
与之相对的是 signed(有符号),这是我们默认的类型,可以存储正数、负数和零。
unsigned 的主要影响是改变了数值的表示范围,而不是改变其大小。
- 有符号整数 (signed): 最高位是符号位(0 表示正,1 表示负),数值范围是
-2^(n-1)到2^(n-1) - 1。 - 无符号整数 (unsigned): 没有符号位,所有位都用来表示数值,数值范围是
0到2^n - 1。
这里的 n 是该类型所占的位数。

unsigned long 的具体特性
unsigned long 的具体特性(如大小和范围)依赖于编译器和操作系统,也就是所谓的“实现定义”,在现代 64 位系统上,它通常是固定的。
大小
- 在 32 位系统上,
long通常是 4 字节(32 位),unsigned long也是 4 字节。 - 在 64 位系统上,
long通常是 8 字节(64 位),unsigned long也是 8 字节。
如何确定你系统上的大小?
你可以使用 sizeof 运算符来查看:
#include <stdio.h>
int main() {
printf("Size of unsigned long: %zu bytes\n", sizeof(unsigned long));
return 0;
}
- 在典型的 64 位 Linux/macOS 系统上,输出会是
Size of unsigned long: 8 bytes。 - 在典型的 64 位 Windows 系统上,
long仍然是 4 字节,unsigned long也是 4 字节,这是 Windows 和类 Unix 系统(Linux/macOS)的一个重要区别。
数值范围
范围同样取决于大小,我们可以使用 <limits.h> 头文件中定义的宏来获取精确值。
| 大小 (字节) | 位数 (n) | 最小值 | 最大值 (宏定义) |
|---|---|---|---|
| 4 | 32 | 0 | ULONG_MAX (通常是 4,294,967,295) |
| 8 | 64 | 0 | ULONG_MAX (通常是 18,446,744,073,709,551,615) |
示例代码:查看 unsigned long 的范围

#include <stdio.h>
#include <limits.h> // 必须包含此头文件才能使用 ULONG_MAX
int main() {
printf("Size of unsigned long: %zu bytes\n", sizeof(unsigned long));
// 使用 %llu 格式说明符来打印 unsigned long long 类型的值
// 因为 ULONG_MAX 在 64 位系统上会非常大,用 unsigned long 可能不够打印
printf("Minimum value of unsigned long: 0\n");
printf("Maximum value of unsigned long (ULONG_MAX): %llu\n", (unsigned long long)ULONG_MAX);
return 0;
}
为什么使用 unsigned long?
选择 unsigned long 而不是 long 或 unsigned int 通常有以下原因:
-
需要更大的正数范围:当你确定一个变量永远不会是负数时,使用
unsigned类型可以将正数范围扩大一倍,一个 4 字节的unsigned long可以存储 0 到 42 亿,而signed long只能存储 -21 亿到 21 亿。 -
处理位运算:在进行位操作(如
&, ,<<,>>)时,使用无符号类型可以避免符号位带来的意外行为,使结果更符合预期。 -
表示内存地址或大小:虽然现代 C 推荐使用
uintptr_t和size_t(定义在<stdint.h>和<stddef.h>中),但unsigned long在历史上常被用来表示内存地址或对象大小。 -
避免算术溢出的警告:在某些情况下,编译器可以更好地检测到无符号整数的下溢(从 0 减 1 变成最大值)或上溢(从最大值加 1 变成 0),从而给出警告。
示例代码
示例 1:基本使用和上溢
#include <stdio.h>
#include <limits.h>
int main() {
unsigned long a = 0;
unsigned long b = ULONG_MAX;
printf("a = %lu\n", a); // %lu 是 unsigned long 的格式说明符
printf("b = %lu\n", b);
// 演示上溢
b = b + 1;
printf("After overflow: b = %lu\n", b); // 结果会回到 0
return 0;
}
输出:
a = 0
b = 18446744073709551615
After overflow: b = 0
示例 2:有符号 vs 无符号的陷阱
这是一个非常经典的面试题,展示了混合使用有符号和无符号整数时的危险。
#include <stdio.h>
int main() {
signed long x = -1;
unsigned long y = 1;
// 当 signed 和 unsigned 数值比较时,signed 数值会被提升为 unsigned
// -1 在 64 位系统上会被解释为一个非常大的无符号数 (2^64 - 1)
if (x > y) {
printf("x is greater than y\n"); // 这行代码会被执行!
} else {
printf("y is greater than x\n");
}
printf("x = %ld, y = %lu\n", x, y);
printf("x as unsigned: %lu\n", (unsigned long)x); // 展示转换后的值
return 0;
}
输出:
x is greater than y
x = -1, y = 1
x as unsigned: 18446744073709551615
解释:当 x (有符号 -1) 和 y (无符号 1) 比较时,编译器会将 x 隐式转换为 unsigned long,由于补码表示,-1 的所有位都是 1,所以转换后的值是 ULONG_MAX,这是一个非常大的数,显然大于 1。这通常是导致 bug 的原因之一。
unsigned long vs 其他类型
| 类型 | 大小 (典型) | 范围 (典型) | 何时使用 |
|---|---|---|---|
unsigned int |
4 字节 | 0 到 4,294,967,295 | 通用无符号整数,范围足够时优先使用。 |
unsigned long |
4 或 8 字节 | 0 到 4,294,967,295 或更大 | 需要平台相关的更大范围,或与系统 API 交互时。 |
unsigned long long |
8 字节 | 0 到 18,446,744,073,709,551,615 | 需要绝对最大的无符号整数范围时,是 C99 标准引入的,保证至少 64 位。 |
size_t |
4 或 8 字节 | 取决于平台 | 表示对象大小或数组索引,这是最推荐的无符号类型,用于内存分配、字符串长度等。 |
unsigned long是 C 语言中用于存储非负整数的类型。- 它的大小和范围是实现定义的,在现代 64 位系统上通常是 8 字节,但 Windows 是个例外(4 字节)。
- 使用
sizeof和<limits.h>中的宏(如ULONG_MAX)可以准确获取其特性。 - 主要优势是提供了比
signed long大一倍的正数范围。 - 注意陷阱:避免将有符号整数 (
signed long) 和无符号整数 (unsigned long) 混合使用,因为这可能导致意外的隐式类型转换和错误的比较结果。 - 对于表示内存大小或索引,现代 C 编程更推荐使用
<stddef.h>中的size_t。
