使用C语言检查类型的最小值和最大值

3

我想知道在C语言中,是否有一种方式可以反射出类型的最大值。

例如,我有一个名为a的变量,它是一个unsigned short类型...

{
    unsigned short a;
    long long max = TYPEOF_MAX(a);
    /* now max will be USHRT_MAX */
}

{
    signed char a;
    long long max = TYPEOF_MAX(a);
    /* now max will be CHAR_MAX */
}

在这里,TYPEOF_MAX是一个宏,它使用某种方法基于输入类型(静态的)来获取范围。

其他限定符...

  • 如果只涉及到一些类型,比如char/short/int/long/long long/float/double,那么也没有关系。
  • 这可能需要使用一些C扩展、GCC或甚至是C11,但更具可移植性的方法更受欢迎。

注意:这是用于生成代码的,显然包括<limits.h>和使用USHRT_MAXCHAR_MAX在几乎所有情况下都可以正常工作。


#include <limits.h> 然后使用 INT_MAX 等。如果您有一个非内置但无符号类型 T,也可以使用 ((T)-1) 来获取最大值。 - The Paramagnetic Croissant
我发表了回答吗? - The Paramagnetic Croissant
2
C11已经三岁了...你越早开始使用它编码,人们就越不会认为它是什么奇怪的东西。事实上,这是偏爱C11解决方案的一个很好的理由。如果它不能正常工作,就去找你的编译器供应商。 - Alex Celeste
4个回答

6

我从未使用过c11,不过它有一个称为“类型通用表达式”的功能,可以帮助您。据维基百科介绍,您可以执行 _Generic(a, int: INT_MAX, long: LONG_MAX, default: SOME_MANINGFUL_DEFAULT_VALUE)。据我所知,它将检查a的类型,并根据其类型选择一些表达式进行评估,这将是类型通用表达式的结果。

这不是最好的解决方案,但您只需要使用_Generic和处理您感兴趣的所有算术类型的宏即可。

GCC 4.9(https://gcc.gnu.org/wiki/C11Status)似乎支持它。

维基百科页面可能比我解释得更好:http://en.wikipedia.org/wiki/C11_(C_standard_revision)


2
这是一种使用GCC的__builtin_types_compatible_p的解决方案,虽然它能够工作,但具有以下缺点:
  • 仅限于GCC(不支持MSVC)
  • 缺少的类型不会在构建时失败(而是在运行时失败)。
  • 无法在函数外部使用。
  • 不被视为常量,所以无法与静态断言(static-assert)一起使用,例如。
样例代码:
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <limits.h>
#include <math.h>
#include <float.h>

#define TYPEOF_MAX(x)                                                         \
  ({                                                                          \
    typeof(x) tmp;                                                            \
    if      (__builtin_types_compatible_p(typeof(x), signed char))            \
      tmp = (typeof(x))CHAR_MAX;                                              \
    else if (__builtin_types_compatible_p(typeof(x), unsigned char))          \
      tmp = (typeof(x))UCHAR_MAX;                                             \
    else if (__builtin_types_compatible_p(typeof(x), signed short))           \
      tmp = (typeof(x))SHRT_MAX;                                              \
    else if (__builtin_types_compatible_p(typeof(x), unsigned short))         \
      tmp = (typeof(x))USHRT_MAX;                                             \
    else if (__builtin_types_compatible_p(typeof(x), signed int))             \
      tmp = (typeof(x))INT_MAX;                                               \
    else if (__builtin_types_compatible_p(typeof(x), unsigned int))           \
      tmp = (typeof(x))UINT_MAX;                                              \
    else if (__builtin_types_compatible_p(typeof(x), signed long))            \
      tmp = (typeof(x))LONG_MAX;                                              \
    else if (__builtin_types_compatible_p(typeof(x), unsigned long))          \
      tmp = (typeof(x))ULONG_MAX;                                             \
    else if (__builtin_types_compatible_p(typeof(x), float))                  \
      tmp = (typeof(x))FLT_MAX;                                               \
    else if (__builtin_types_compatible_p(typeof(x), double))                 \
      tmp = (typeof(x))DBL_MAX;                                               \
    else                                                                      \
      abort ();                                                               \
    tmp;                                                                      \
  })


int main(void)
{
    short       num_short;
    int         num_int;
    double      num_double;
    signed char num_char;


    printf("  %ld max short\n",     TYPEOF_MAX(num_short));
    printf("  %ld max int\n",       TYPEOF_MAX(num_int));
    printf("  %f  max double\n",    TYPEOF_MAX(num_double));
    printf("  %ld max char\n",      TYPEOF_MAX(num_char));
    return 0;
}

2

这个例子基于@Pedro Henrique A. Oliveira的答案使用了C11泛型。

注意,它还可以添加更多类型(ssize_t size_t intptr_t等)。

#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
#include <float.h>
#include <stdint.h>

#define TYPEOF_MAX(x) \
    _Generic(x, \
        bool: 1, \
        char: CHAR_MAX, signed char: SCHAR_MAX, unsigned char: UCHAR_MAX, \
        signed short: SHRT_MAX, unsigned short: USHRT_MAX, \
        signed int: INT_MAX, unsigned int: UINT_MAX, \
        signed long: LONG_MAX, unsigned long: ULONG_MAX, \
        signed long long: LLONG_MAX, unsigned long long: ULLONG_MAX, \
        float: FLT_MAX, double: DBL_MAX)


#define TYPEOF_MIN(x) \
    _Generic(x, \
        bool: 0, \
        char: CHAR_MIN, signed char: SCHAR_MIN, unsigned char: 0, \
        signed short: SHRT_MIN, unsigned short: 0, \
        signed int: INT_MIN, unsigned int: 0, \
        signed long: LONG_MIN, unsigned long: 0, \
        signed long long: LLONG_MIN, unsigned long long: 0, \
        float: -FLT_MAX, double: -DBL_MAX)

/* change 100 to 1000 - static asserts work! */
_Static_assert(TYPEOF_MAX((char)4) > 100, "example check");


int main(void)
{
    short       num_short;
    int         num_int;
    double      num_double;
    signed char num_char;


    printf("  %ld max short\n",     TYPEOF_MAX(num_short));
    printf("  %ld max int\n",       TYPEOF_MAX(num_int));
    printf("  %f  max double\n",    TYPEOF_MAX(num_double));
    printf("  %ld max char\n",      TYPEOF_MAX(num_char));
    return 0;
}

0

如果你愿意对数字表示做出假设,而不陷入支持你几乎不可能关心的架构标准中,你可以将~0放入目标类型的变量(对于无符号类型),然后将该值赋给max。 对于有符号(或潜在有符号)类型,您插入~0并检查a <0。 如果是这样,请假定为2的补码,从那里开始。 您已经决定long long可以保存您关心的任何类型的任何值,因此可以在破坏它之前存储a的副本(假设只给出a即可使此技巧起作用)。

我刚刚编写了一个测试示例来验证所有这些使用普通优化都能简化为常数(使用gcc):

a = ~0;
if (a < 0)  // true if a is signed
{
    a ^= 1 << (sizeof(a) * 8 - 1);  // flip sign bit to get all 1's positive
    max = a;
    min = -a - 1;
}
else
{
    max = a;
    min = 0;
}

这可以适用于最大值,但最小值呢?同时还能适用于有符号/无符号吗? - ideasman42

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