【问题标题】:Introspect type min/max with C用 C 内省类型 min/max
【发布时间】:2014-07-28 05:30:00
【问题描述】:

我想知道 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 是一个宏,它使用某种方法根据 in 类型(静态)获取范围。

其他限定词...

  • 如果仅限于几种类型,则可以,例如char/short/int/long/long long/float/double
  • 这可能需要使用一些 C 扩展、GCC 甚至可能是 C11,但更可移植的方法是首选。

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

【问题讨论】:

  • #include <limits.h> 然后使用INT_MAX 等。如果你有一个非内置但无符号类型T,你也可以使用((T)-1) 来获取最大值。
  • 和?我把它作为答案发布了吗?
  • C11 已经 3 岁了...你越早在其中编写代码,人们就会越早不再认为它是什么奇怪的东西。事实上,这是选择 C11 解决方案的一个很好的理由。如果它不起作用,请向您的编译器供应商发送错误。

标签: c types c-preprocessor


【解决方案1】:

我从来没有做过 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;
    }
    

    【讨论】:

      【解决方案3】:

      此示例使用基于@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;
      }
      

      【讨论】:

        【解决方案4】:

        如果您愿意对数字表示做出假设,而不是陷入标准(支持您几乎不可能关心的架构),您可以将 ~0 填充到目标类型的变量中(对于无符号类型),然后将该值分配给max。对于已签名(或可能已签名)的类型,请填写 ~0 并检查 a &lt; 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;
        }
        

        【讨论】:

        • 这可以用于最大值,但是最小值和签名/未签名的工作呢?
        猜你喜欢
        • 2013-03-09
        • 1970-01-01
        • 2017-07-25
        • 1970-01-01
        • 2015-09-25
        • 1970-01-01
        • 1970-01-01
        • 2010-12-10
        • 1970-01-01
        相关资源
        最近更新 更多