C语言中数据类型的最小值和最大值是什么?

84

在C语言中,确定数据类型(如int、char等)的最小和最大可能值的函数是什么?

10个回答

100

你需要使用 limits.h 库,该库提供了以下常量(参考链接所示):

SCHAR_MIN      : minimum value for a signed char
SCHAR_MAX      : maximum value for a signed char
UCHAR_MAX      : maximum value for an unsigned char
CHAR_MIN       : minimum value for a char
CHAR_MAX       : maximum value for a char
SHRT_MIN       : minimum value for a short
SHRT_MAX       : maximum value for a short
USHRT_MAX      : maximum value for an unsigned short
INT_MIN        : minimum value for an int
INT_MAX        : maximum value for an int
UINT_MAX       : maximum value for an unsigned int
LONG_MIN       : minimum value for a long
LONG_MAX       : maximum value for a long
ULONG_MAX      : maximum value for an unsigned long
LLONG_MIN      : minimum value for a long long
LLONG_MAX      : maximum value for a long long
ULLONG_MAX     : maximum value for an unsigned long long
PTRDIFF_MIN    : minimum value of ptrdiff_t
PTRDIFF_MAX    : maximum value of ptrdiff_t
SIZE_MAX       : maximum value of size_t
SIG_ATOMIC_MIN : minimum value of sig_atomic_t
SIG_ATOMIC_MAX : maximum value of sig_atomic_t
WINT_MIN       : minimum value of wint_t
WINT_MAX       : maximum value of wint_t
WCHAR_MIN      : minimum value of wchar_t
WCHAR_MAX      : maximum value of wchar_t
CHAR_BIT       : number of bits in a char
MB_LEN_MAX     : maximum length of a multibyte character in bytes

当然不必写 U*_MIN,因为任何无符号类型的最小值都是 0

类似地,float.h 提供了 floatdouble 类型的限制:

FLT_MIN    : smallest normalised positive value of a float
FLT_MAX    : largest positive finite value of a float
DBL_MIN    : smallest normalised positive value of a double
DBL_MAX    : largest positive finite value of a double
LDBL_MIN   : smallest normalised positive value of a long double
LDBL_MAX   : largest positive finite value of a long double
FLT_DIG    : the number of decimal digits guaranteed to be preserved converting from text to float and back to text
DBL_DIG    : the number of decimal digits guaranteed to be preserved converting from text to double and back to text
LDBL_DIG   : the number of decimal digits guaranteed to be preserved converting from text to long double and back to text

浮点类型在零周围是对称的,因此最小有限数是最大有限数的相反数 - 例如 float 范围从 -FLT_MAXFLT_MAX

请注意,浮点类型只能准确表示其范围内的少量有限值。随着存储的绝对值越来越大,可以精确表示的相邻数字之间的间距也变得更大。


2
浮点数的最小值和最大值是多少? - SuperString
4
SIZE_MAXsize_t 类型的最大值)也是一个有用的常量。 - caf
@MartinBeckett 不是根据头文件或我在C语言中编写的记忆吗?FLT_MIN近似于零,不是吗? - Adam
调整后;但您需要注意头文件确实包含标记为min和max的两者。 - Mark Elliot
1
@mirabilos:没错,你的代码似乎假定了 struct timeval,这意味着你处于一个 time_t 是整数的世界。尽管如此,你可以在已知类型(比如 unsigned long long)中进行输入解析,然后转换为 time_t 并测试结果是否等于解析的 unsigned long long 值。将超出范围的值转换为类型不会导致未定义行为,即使对于有符号类型也是如此(虽然理论上可能会导致“引发实现定义的信号”,但我不认为 POSIX 允许这样做)。 - caf
显示剩余10条评论

39
“但是字符”,我听到你在问,“如果我必须确定一个不透明类型的最大值,而其最大值可能最终会改变怎么办?” 你可能会继续说:“如果它是一个库中的typedef,我无法控制该怎么办?”
我很高兴你问了这个问题,因为我刚花了几个小时想出了一个解决方案(然后不得不放弃,因为它没有解决我的实际问题)。
您可以使用这个方便的maxof宏来确定任何有效整数类型的大小。
#define issigned(t) (((t)(-1)) < ((t) 0))

#define umaxof(t) (((0x1ULL << ((sizeof(t) * 8ULL) - 1ULL)) - 1ULL) | \
                    (0xFULL << ((sizeof(t) * 8ULL) - 4ULL)))

#define smaxof(t) (((0x1ULL << ((sizeof(t) * 8ULL) - 1ULL)) - 1ULL) | \
                    (0x7ULL << ((sizeof(t) * 8ULL) - 4ULL)))

#define maxof(t) ((unsigned long long) (issigned(t) ? smaxof(t) : umaxof(t)))

您可以这样使用它:

int main(int argc, char** argv) {
    printf("schar: %llx uchar: %llx\n", maxof(char), maxof(unsigned char));
    printf("sshort: %llx ushort: %llx\n", maxof(short), maxof(unsigned short));
    printf("sint: %llx uint: %llx\n", maxof(int), maxof(unsigned int));
    printf("slong: %llx ulong: %llx\n", maxof(long), maxof(unsigned long));
    printf("slong long: %llx ulong long: %llx\n",
           maxof(long long), maxof(unsigned long long));
    return 0;
}

如果您愿意,可以在这些宏的前面加上“(t)”,这样它们就会给您提供所要求的类型结果,而无需进行强制转换以避免警告。

2
所有这些8ULL的常量可能应该是CHAR_BIT。 - jschultz410
1
在maxof条件性地使用正确的宏取决于issigned方面,非常赞! - jschultz410
1
@jschultz410 您编写的smaxof(t)((t) ~(1ULL << (sizeof(t) * CHARBIT - 1))),对于大于long long的整数类型将无法正常工作。您可以使用(uintmax_t),但请注意,实际上,我们可能有比uintmax_t更大的整数类型扩展,例如GCC的__int128。我的解决方案避免了这种问题,并且应该适用于没有uintmax_t的C实现。 - vinc17
2
smaxof(t) 假设 1) 没有填充,2) 每个 "字节" 包含 8 位。这些是合理的假设,但 C 语言没有明确规定。 - chux - Reinstate Monica
1
umaxof(t) 假设没有比 unsigned long long 更宽的类型。这是一个合理的假设,但 C 语言并没有明确规定。使用 uintmax_t 更为合理。 - chux - Reinstate Monica
显示剩余4条评论

10

无符号整数类型的最大值:

  • ((t)~(t)0) // 通用表达式,几乎适用于所有情况。

  • (~(t)0) // 如果您知道类型t具有相等或更大的大小,则可以使用此方法。这个强制转换会引起类型提升。

  • ((t)~0U) // 如果您知道类型t具有相等或更小的大小,则可以使用此方法。这个强制转换在求得 unsigned int 类型表达式 ~0U 的值之后降级类型。

例如:可以将size_t类型(也称为SIZE_MAX宏)的最大值定义为(~(size_t)0)Linux内核源代码以这种方式定义SIZE_MAX宏。

有符号整数类型的最大值:

  • 如果您有一个类型t的无符号变体,则((t)(((unsigned t)~(unsigned t)0)>>1))可以给出最快的结果。

  • 否则,使用此方法(感谢@vinc17提供建议):(((t)1<<(sizeof(t)*CHAR_BIT-2))-1)*2+1

有符号整数类型的最小值:

您必须了解机器上的有符号数表示方式。大多数计算机使用二进制补码,所以这将适用于您:

-((((t)1<<(sizeof(t)*CHAR_BIT-2))-1)*2+1)-1

/* Equivalent: */
(1-((t)1<<(sizeof(t)*CHAR_BIT-2)))*2-2

要检测您的计算机是否使用二进制补码,请检测((t)~(t)0)(t)(-1)是否表示相同的内容。更新(2022年3月24日):(t)应该是带符号类型。C99标准规定了从有符号整数类型到无符号整数类型的转换行为。不管有符号整数类型中的值的对象表示如何,将-N转换为无符号值总是产生无符号类型的最大值加1减去N的结果。例如,(unsigned long)(-(N))等于ULONG_MAX+1-(N)

结合上述信息,这将给出任何有符号整数类型的最小值:

-((((t)1<<(sizeof(t)*CHAR_BIT-2))-1)*2+1)-((t)~(t)0==(t)-1)

/* Equivalent: */
(1-((t)1<<(sizeof(t)*CHAR_BIT-2)))*2-1-((t)~(t)0==(t)-1)

然而,需要注意的一点是:所有这些表达式都使用了类型转换或sizeof运算符,因此都不能在预处理条件中使用(如#if ... #elif ... #endif等)。

(感谢 @chux 和 @vinc17 的建议,已更新答案。)


此处的“有符号整数类型的最大值”方法依赖于假设,尽管这些假设非常普遍。请注意,虽然不常见,但在C语言中允许xxx_MAX == Uxxx_MAX以及xxx_MAX < Uxxx_MAX/2。具体规定是xxx_MAX <= Uxxx_MAX且两种类型具有相同的大小。 - chux - Reinstate Monica
据我所知,char是唯一可能满足xxx_MAX == Uxxx_MAX的C标准类型,因为char可以根据实现而有符号或无符号。对于xxx_MAX < Uxxx_MAX/2的情况,最有可能是由于非二进制补码算术引起的(否则对于一个实现来说就没有意义了)。 - Explorer09
1
(t)0int更窄时,~((t) 0)无法正常工作。 - chux - Reinstate Monica
1
@chux 感谢您提供关于 ~((t) 0) 的提示。至于 xxx_MAX == Uxxx_MAXxxx_MAX < Uxxx_MAX/2 的情况,根据我在 C99 标准中所读到的,是允许的。 - Explorer09
1ULL 在一些不支持C99的编译器中会出错。我们能不能只用((t)1)呢? - mirabilos
显示剩余2条评论

3
我编写了一些宏,可以返回任何类型的最小值和最大值,无论其是否带符号:
#define MAX_OF(type) \
    (((type)(~0LLU) > (type)((1LLU<<((sizeof(type)<<3)-1))-1LLU)) ? (long long unsigned int)(type)(~0LLU) : (long long unsigned int)(type)((1LLU<<((sizeof(type)<<3)-1))-1LLU))
#define MIN_OF(type) \
    (((type)(1LLU<<((sizeof(type)<<3)-1)) < (type)1) ? (long long int)((~0LLU)-((1LLU<<((sizeof(type)<<3)-1))-1LLU)) : 0LL)

示例代码:

#include <stdio.h>
#include <sys/types.h>
#include <inttypes.h>

#define MAX_OF(type) \
    (((type)(~0LLU) > (type)((1LLU<<((sizeof(type)<<3)-1))-1LLU)) ? (long long unsigned int)(type)(~0LLU) : (long long unsigned int)(type)((1LLU<<((sizeof(type)<<3)-1))-1LLU))
#define MIN_OF(type) \
    (((type)(1LLU<<((sizeof(type)<<3)-1)) < (type)1) ? (long long int)((~0LLU)-((1LLU<<((sizeof(type)<<3)-1))-1LLU)) : 0LL)

int main(void)
{
    printf("uint32_t = %lld..%llu\n", MIN_OF(uint32_t), MAX_OF(uint32_t));
    printf("int32_t = %lld..%llu\n", MIN_OF(int32_t), MAX_OF(int32_t));
    printf("uint64_t = %lld..%llu\n", MIN_OF(uint64_t), MAX_OF(uint64_t));
    printf("int64_t = %lld..%llu\n", MIN_OF(int64_t), MAX_OF(int64_t));
    printf("size_t = %lld..%llu\n", MIN_OF(size_t), MAX_OF(size_t));
    printf("ssize_t = %lld..%llu\n", MIN_OF(ssize_t), MAX_OF(ssize_t));
    printf("pid_t = %lld..%llu\n", MIN_OF(pid_t), MAX_OF(pid_t));
    printf("time_t = %lld..%llu\n", MIN_OF(time_t), MAX_OF(time_t));
    printf("intptr_t = %lld..%llu\n", MIN_OF(intptr_t), MAX_OF(intptr_t));
    printf("unsigned char = %lld..%llu\n", MIN_OF(unsigned char), MAX_OF(unsigned char));
    printf("char = %lld..%llu\n", MIN_OF(char), MAX_OF(char));
    printf("uint8_t = %lld..%llu\n", MIN_OF(uint8_t), MAX_OF(uint8_t));
    printf("int8_t = %lld..%llu\n", MIN_OF(int8_t), MAX_OF(int8_t));
    printf("uint16_t = %lld..%llu\n", MIN_OF(uint16_t), MAX_OF(uint16_t));
    printf("int16_t = %lld..%llu\n", MIN_OF(int16_t), MAX_OF(int16_t));
    printf("int = %lld..%llu\n", MIN_OF(int), MAX_OF(int));
    printf("long int = %lld..%llu\n", MIN_OF(long int), MAX_OF(long int));
    printf("long long int = %lld..%llu\n", MIN_OF(long long int), MAX_OF(long long int));
    printf("off_t = %lld..%llu\n", MIN_OF(off_t), MAX_OF(off_t));

    return 0;
}

3
头文件limits.h定义了一些宏,这些宏会扩展为标准整数类型的各种限制和参数。

无符号字符的最小值是多少? - SuperString
5
@Superstring,任何无符号类型的最小值为0。 - Mark Elliot
4
我想要负的未签名值! :-) - Alok Singhal

3
#include<stdio.h>

int main(void)
{
    printf("Minimum Signed Char %d\n",-(char)((unsigned char) ~0 >> 1) - 1);
    printf("Maximum Signed Char %d\n",(char) ((unsigned char) ~0 >> 1));

    printf("Minimum Signed Short %d\n",-(short)((unsigned short)~0 >>1) -1);
    printf("Maximum Signed Short %d\n",(short)((unsigned short)~0 >> 1));

    printf("Minimum Signed Int %d\n",-(int)((unsigned int)~0 >> 1) -1);
    printf("Maximum Signed Int %d\n",(int)((unsigned int)~0 >> 1));

    printf("Minimum Signed Long %ld\n",-(long)((unsigned long)~0 >>1) -1);
    printf("Maximum signed Long %ld\n",(long)((unsigned long)~0 >> 1));

    /* Unsigned Maximum Values */

    printf("Maximum Unsigned Char %d\n",(unsigned char)~0);
    printf("Maximum Unsigned Short %d\n",(unsigned short)~0);
    printf("Maximum Unsigned Int %u\n",(unsigned int)~0);
    printf("Maximum Unsigned Long %lu\n",(unsigned long)~0);

    return 0;
}

我们可以通过获取无符号数据类型的最大值并从该值中减去最大值来获得最小值。 - Akansh
1
这是一个非常好的、系统无关的答案,展示了对类型、内存以及C位运算符的理解。 - Jonathan Komar
1
@JonathanKomar 上述所有有符号的最小值都假定采用2的补码架构,这通常(但并非总是)在C语言中使用。 - jschultz410
1
更正:依赖于系统(假设位的解释是二进制补码)。感谢jschultz410。 - Jonathan Komar

2
为了获得至少与 unsigned int 宽度相同的无符号整数类型 t 的最大值(否则会出现整数提升问题): ~(t) 0。如果还想支持更短的类型,则可以添加另一个强制转换: (t) ~(t) 0
如果整数类型 t 是有符号的,假设没有填充位,则可以使用:
((((t) 1 << (sizeof(t) * CHAR_BIT - 2)) - 1) * 2 + 1)

这个公式的优点在于它不是基于某个未签名版本的t(或更大的类型),这可能是未知或不可用的(即使uintmax_t在非标准扩展中也可能不足够)。以下是一个6位的示例(实际上不可能,只是为了易读性):
010000  (t) 1 << (sizeof(t) * CHAR_BIT - 2)
001111  - 1
011110  * 2
011111  + 1

在二进制补码中,最小值是最大值的相反数减1(在ISO C标准允许的其他整数表示中,这只是最大值的相反数)。
注意:为了检测有符号性以决定使用哪个版本:(t) -1 < 0 可以适用于任何整数表示,对于有符号整数类型返回1(true),对于无符号整数类型返回0(false)。因此可以使用以下方式:
(t) -1 < 0 ? ((((t) 1 << (sizeof(t) * CHAR_BIT - 2)) - 1) * 2 + 1) : (t) ~(t) 0

对于 signed max,为什么不更简单地使用 (~((t) 1 << (sizeof(t) * CHAR_BIT - 1)))? - jschultz410
1
@jschultz410 因为这是未定义行为。数学上(且为正值)的2到sizeof(t) * CHAR_BIT - 1不可在有符号类型t中表示。您假设左移的“包装”行为,这不是标准行为(并且可能会在优化编译器中失败),并且甚至在不同于二进制补码的整数表示中也没有意义(如C标准所允许的)。 - vinc17
为什么不直接使用-1来表示最大无符号类型,而不是"(t) ~(t) 0"呢?以下所有的写法都是正确的:unsigned char cmax = -1; unsigned short smax = -1; unsigned max = -1; unsigned long lmax = -1; unsigned long long llmax = -1; - undefined
@ZeZNiQ 是的,(t) -1 可以使用(特别是在最后一个表达式中,我已经在条件中使用它了),而且会稍微简单一些。我记得当初我曾经想过这个会不会触发一些编译器的警告(尽管通过强制转换,实际上并没有真正的理由)。请注意,对于赋值操作,即使强制转换是无用的,添加一个也可以用来消除一些警告,比如 GCC 的 -Wconversion - undefined

2

1

只是为了补充其他答案,可以提到C99在stdint.h头文件中定义了诸如INT8_MAXUINT8_MAXINT16_MAXUINT16_MAX等宏。例如,请参阅此链接


0

任何整数数据类型的最小值和最大值都可以通过以下方式计算,而且相同的逻辑也可以应用于其他整数类型,如short、int和long,无需使用任何库函数。

printf("Signed Char : MIN -> %d & Max -> %d\n", ~(char)((unsigned char)~0>>1), (char)((unsigned char)~0 >> 1));
printf("Unsigned Char : MIN -> %u & Max -> %u\n", (unsigned char)0, (unsigned char)(~0));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接