unsigned long取值范围是多少?

99ANYc3cd6
预计阅读时长 15 分钟
位置: 首页 C语言 正文

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

c语言 unsigned long
(图片来源网络,侵删)

核心概念:unsigned 的含义

在 C 语言中,unsigned 关键字表示“无符号”,意思是这个类型的变量不能存储负数

与之相对的是 signed(有符号),这是我们默认的类型,可以存储正数、负数和零。

unsigned 的主要影响是改变了数值的表示范围,而不是改变其大小。

  • 有符号整数 (signed): 最高位是符号位(0 表示正,1 表示负),数值范围是 -2^(n-1)2^(n-1) - 1
  • 无符号整数 (unsigned): 没有符号位,所有位都用来表示数值,数值范围是 02^n - 1

这里的 n 是该类型所占的位数。

c语言 unsigned long
(图片来源网络,侵删)

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 的范围

c语言 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 而不是 longunsigned int 通常有以下原因:

  1. 需要更大的正数范围:当你确定一个变量永远不会是负数时,使用 unsigned 类型可以将正数范围扩大一倍,一个 4 字节的 unsigned long 可以存储 0 到 42 亿,而 signed long 只能存储 -21 亿到 21 亿。

  2. 处理位运算:在进行位操作(如 &, , <<, >>)时,使用无符号类型可以避免符号位带来的意外行为,使结果更符合预期。

  3. 表示内存地址或大小:虽然现代 C 推荐使用 uintptr_tsize_t(定义在 <stdint.h><stddef.h> 中),但 unsigned long 在历史上常被用来表示内存地址或对象大小。

  4. 避免算术溢出的警告:在某些情况下,编译器可以更好地检测到无符号整数的下溢(从 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 字节 取决于平台 表示对象大小或数组索引,这是最推荐的无符号类型,用于内存分配、字符串长度等。
  1. unsigned long 是 C 语言中用于存储非负整数的类型。
  2. 它的大小和范围是实现定义的,在现代 64 位系统上通常是 8 字节,但 Windows 是个例外(4 字节)。
  3. 使用 sizeof<limits.h> 中的宏(如 ULONG_MAX)可以准确获取其特性。
  4. 主要优势是提供了比 signed long 大一倍的正数范围。
  5. 注意陷阱:避免将有符号整数 (signed long) 和无符号整数 (unsigned long) 混合使用,因为这可能导致意外的隐式类型转换和错误的比较结果。
  6. 对于表示内存大小或索引,现代 C 编程更推荐使用 <stddef.h> 中的 size_t
-- 展开阅读全文 --
头像
dede如何调用顶级栏目下的二级栏目?
« 上一篇 今天
dede二级栏目及文章标题如何调用?
下一篇 » 今天
取消
微信二维码
支付宝二维码

目录[+]