取值范围与 limits.h
C 语言标准只规定了整数类型的最小范围,具体大小由编译器和平台决定。<limits.h> 头文件提供了一组宏,让你能在代码中查询当前环境的实际取值范围,从而写出适应不同平台的健壮代码。
limits.h 宏
<limits.h> 定义了各整数类型的最大值和最小值:
| 宏 | 含义 | 典型值(32 位平台) |
|---|---|---|
CHAR_BIT | char 的位数 | 8 |
SCHAR_MIN | signed char 最小值 | -128 |
SCHAR_MAX | signed char 最大值 | 127 |
UCHAR_MAX | unsigned char 最大值 | 255 |
CHAR_MIN | char 最小值(可能等于 SCHAR_MIN 或 0) | -128 或 0 |
CHAR_MAX | char 最大值(可能等于 SCHAR_MAX 或 UCHAR_MAX) | 127 或 255 |
SHRT_MIN | short 最小值 | -32768 |
SHRT_MAX | short 最大值 | 32767 |
USHRT_MAX | unsigned short 最大值 | 65535 |
INT_MIN | int 最小值 | -2147483648 |
INT_MAX | int 最大值 | 2147483647 |
UINT_MAX | unsigned int 最大值 | 4294967295 |
LONG_MIN | long 最小值 | -2147483648 或更大 |
LONG_MAX | long 最大值 | 2147483647 或更大 |
ULONG_MAX | unsigned long 最大值 | 4294967295 或更大 |
LLONG_MIN | long long 最小值(C99) | -9223372036854775808 |
LLONG_MAX | long long 最大值(C99) | 9223372036854775807 |
ULLONG_MAX | unsigned long long 最大值(C99) | 18446744073709551615 |
#include <stdio.h>
#include <limits.h>
int main(void)
{
printf("CHAR_BIT = %d\n", CHAR_BIT);
printf("INT_MIN = %d\n", INT_MIN);
printf("INT_MAX = %d\n", INT_MAX);
printf("UINT_MAX = %u\n", UINT_MAX);
printf("LONG_MIN = %ld\n", LONG_MIN);
printf("LONG_MAX = %ld\n", LONG_MAX);
printf("LLONG_MIN = %lld\n", LLONG_MIN);
printf("LLONG_MAX = %lld\n", LLONG_MAX);
return 0;
}
判断 char 是否有符号
char 是否有符号是实现定义的,可以用 CHAR_MIN 判断:
#include <limits.h>
#include <stdio.h>
int main(void)
{
if (CHAR_MIN < 0)
printf("char is signed (%d to %d)\n", CHAR_MIN, CHAR_MAX);
else
printf("char is unsigned (0 to %d)\n", CHAR_MAX);
return 0;
}
如果需要明确的语义,直接写 signed char 或 unsigned char,不依赖 char 的默认属性。
防止整数溢出
在进行可能溢出的运算前,检查操作数范围:
#include <limits.h>
#include <stdio.h>
/* 安全加法:检查溢出 */
int safe_add(int a, int b)
{
if (b > 0 && a > INT_MAX - b) {
printf("Overflow!\n");
return INT_MAX; /* 或返回错误码 */
}
if (b < 0 && a < INT_MIN - b) {
printf("Underflow!\n");
return INT_MIN;
}
return a + b;
}
int main(void)
{
printf("%d\n", safe_add(INT_MAX, 1)); /* 检测到溢出 */
printf("%d\n", safe_add(100, 200)); /* 300 */
return 0;
}
INT_MAX - b 的技巧避免了在检查过程中就发生溢出。如果 b > 0 且 a > INT_MAX - b,那么 a + b 必然大于 INT_MAX。
float.h:浮点类型的范围
<float.h> 提供了浮点类型的特征值:
| 宏 | 含义 |
|---|---|
FLT_MIN | float 最小正值 |
FLT_MAX | float 最大正值 |
FLT_EPSILON | float 最小精度(1.0 + ε ≠ 1.0) |
DBL_MIN | double 最小正值 |
DBL_MAX | double 最大正值 |
DBL_EPSILON | double 最小精度 |
#include <float.h>
#include <stdio.h>
int main(void)
{
printf("FLT_MIN = %e\n", FLT_MIN);
printf("FLT_MAX = %e\n", FLT_MAX);
printf("FLT_EPSILON = %e\n", FLT_EPSILON);
printf("DBL_MIN = %e\n", DBL_MIN);
printf("DBL_MAX = %e\n", DBL_MAX);
printf("DBL_EPSILON = %e\n", DBL_EPSILON);
return 0;
}
FLT_EPSILON 约等于 1.19×10⁻⁷,表示 float 能区分的最小差值。如果两个 float 的差小于 FLT_EPSILON,它们可能被当作相等:
#include <float.h>
#include <math.h>
#include <stdio.h>
int float_equal(float a, float b)
{
return fabs(a - b) < FLT_EPSILON;
}
跨平台的定宽类型
如果代码需要精确控制整数大小(如网络协议、文件格式解析),<stdint.h>(C99 新增)提供了定宽类型:
#include <stdint.h>
#include <stdio.h>
int main(void)
{
int8_t i8; /* 保证 8 位 */
int16_t i16; /* 保证 16 位 */
int32_t i32; /* 保证 32 位 */
int64_t i64; /* 保证 64 位 */
uint8_t u8; /* 保证 8 位无符号 */
uint16_t u16; /* 保证 16 位无符号 */
uint32_t u32; /* 保证 32 位无符号 */
uint64_t u64; /* 保证 64 位无符号 */
printf("sizeof(int32_t) = %zu\n", sizeof(int32_t)); /* 总是 4 */
printf("sizeof(int64_t) = %zu\n", sizeof(int64_t)); /* 总是 8 */
return 0;
}
<stdint.h> 还提供了对应类型的极限宏:
#include <stdint.h>
printf("INT32_MIN = %" PRId32 "\n", INT32_MIN);
printf("INT32_MAX = %" PRId32 "\n", INT32_MAX);
printf("UINT32_MAX = %" PRIu32 "\n", UINT32_MAX);
这些类型和宏消除了 int、long 等平台相关类型的大小不确定性,是编写可移植代码的首选。
常见用法
分配足够大的缓冲区:
char buffer[INT_MAX / 1024]; /* 危险:可能太大 */
/* 更安全的做法 */
#define BUFFER_SIZE 1024
char buffer[BUFFER_SIZE];
循环边界检查:
for (int i = 0; i < count; i++) {
if (i == INT_MAX) { /* 防止无限循环 */
printf("Limit reached\n");
break;
}
/* ... */
}