【问题标题】:Get the maximum value of a variable in C获取C中变量的最大值
【发布时间】:2012-10-06 17:23:01
【问题描述】:

C 中有没有这样的函数返回变量的最大值(我将在下面的示例中将函数命名为“maxvalue”)?

int a;
printf("%d", maxvalue(a)); // 32767
unsigned int b;
printf("%d", maxvalue(b)); // 65535

所以基本上,当变量为有符号 INT 时,该函数返回 INT_MAX 之类的值,当无符号 int 时返回 UINT_MAX 等。

【问题讨论】:

  • 新的 C11 可能有type-generic macros
  • C 函数作为参数值,而不是类型。对于无符号整数类型T,您始终可以使用((T)-1) 计算该类型的最大值。对于有符号整数类型,我不知道任何可移植的解决方案。
  • @Joachim - 绝对有可能。我的回答有一个完整的 OP 案例示例。我的系统有 32 位的int 类型,所以答案更大,但思路是一样的。
  • @CarlNorum 甚至可以不用 C11;看我的回答(后半部分)。
  • @CarlNorum ... 与否;看看我的事后想法。

标签: c


【解决方案1】:

C 标准库没有定义这样的函数。您可以尝试定义一个计算它的宏:

#define MAX_VALUE(a) (((unsigned long long)1 << (sizeof(a) * CHAR_BIT)) - 1)

使用它时,请注意将其分配给足够大的类型。例如:

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

#define IS_TYPE_SIGNED(a) ((a-1) < 0)
#define MAX_VALUE_UNSIGNED(a) (((unsigned long long)1 << \
        (sizeof(a) * CHAR_BIT)) - 1)
#define MAX_VALUE_SIGNED(a) (MAX_VALUE_UNSIGNED(a) >> 1)
#define MAX_VALUE(a) (IS_TYPE_SIGNED(a) ? \
        MAX_VALUE_SIGNED(a) : MAX_VALUE_UNSIGNED(a))

int main(void)
{
    unsigned int i = 0;
    signed int j = 0;

    printf("%llu\n", MAX_VALUE(i));
    printf("%llu\n", MAX_VALUE(j));
    return EXIT_SUCCESS;
}

打印出来:

4294967295
2147483647

【讨论】:

  • 它需要一个-1。无论如何,这个问题的答案很好。
  • 它确实适用于签名类型 - 运行上面的程序会在我的机器上打印出 4294967296,这听起来很正确。
  • @Mike - 不,它不适用于签名类型。我否决了您的答案,因为它是错误的。 4294967295 几乎可以肯定是 unsigned 类型的最大值。有符号整数的 MAX_VALUE 可能应该返回 2147483647。最大有符号 32 位数字是 0x7fffffff,而不是 0xffffffff,您的答案给出了。
  • 对不起 - 读错了括号。您的程序返回0x8000000,这仍然是错误的,但方式不同。它既不是INT_MAX,也不是UINT_MAX。我猜是-INT_MIN?也有奇怪的宏错误 - 你的 (unsigned long long) 演员只是将 1 投射到你的宏中,而不是整个结果,这是它似乎完全有效的唯一原因 - 如果你有,你会得到一个否定的结果宏扩展周围的括号。
  • (((unsigned long long)1 &lt;&lt; (sizeof(a) * CHAR_BIT)) - 1) 不适用于 a's 类型的 (unsigned) long long,因为移位运算符计数溢出。这是未定义的行为。
【解决方案2】:

您可以使用C11 type-generic expression 轻松做到这一点:

#define maxvalue(type) _Generic(type, int: INT_MAX, \
                                      unsigned int: UINT_MAX)

这不是一个功能,但我认为它可以满足您的需求。这是一个简单的示例程序:

#include <stdio.h>
#include <limits.h>

#define maxvalue(type) _Generic(type, int: INT_MAX, \
                                      unsigned int: UINT_MAX)

int main(void)
{
    int i;
    unsigned int ui;

    printf("%u\n", maxvalue(i));
    printf("%u\n", maxvalue(ui));

    return 0;
}

及其输出:

$ clang -Wall -Werror -Wextra -pedantic -std=c11 example.c -o example
$ ./example 
2147483647
4294967295

我的答案比你的大,因为我的系统有 32 位整数。您似乎有一台 16 位机器。

【讨论】:

  • 我不知道你可以让 c 告诉你变量的类型。整洁。
  • @AndyHarris - 相当新的功能。我什至不确定哪些编译器具有良好的 C11 支持。我主要使用 clang/LLVM,它似乎处理得很好。
【解决方案3】:

以下是我的库中适用于类型而非变量的宏:

/* min and max integer values.  T is a signed or unsigned integer type. */

/* Returns 1 if T is signed, else 0. */
#define INTTYPE_SIGNED(T) ((T)-1 < (T)0)

/*
 * Returns (T)(maximum value of a T).
 *
 * Pains are taken (perhaps unnecessarily) to avoid integer overflow
 * with signed types.
 */
#define INTTYPE_MAX(T)                      \
    (((T)1 << (CHAR_BIT*sizeof(T)-INTTYPE_SIGNED(T)-1)) - 1 +   \
     ((T)1 << (CHAR_BIT*sizeof(T)-INTTYPE_SIGNED(T)-1)))

/*
 * Returns (T)(minimum value of a T).

 * Pains are taken (perhaps unnecessarily) to avoid integer overflow
 * with signed types.
 * assert: twos complement architecture
 */
#define INTTYPE_MIN(T) ((T)(-INTTYPE_MAX(T)-1))

编辑: 使这些适应问题:

/* min and max integer values.  V is a signed or unsigned integer value. */

/* Returns 1 if V has signed type, else 0. */
#define INT_VALUE_SIGNED(V) ((V)-(V)-1 < 0)

/*
 * Returns maximum value for V's type.
 *
 * Pains are taken (perhaps unnecessarily) to avoid integer overflow
 * with signed types.
 */
#define INT_VALUE_MAX(V)                        \
    (((V)-(V)+1 << (CHAR_BIT*sizeof(V)-INT_VALUE_SIGNED(V)-1)) - 1 +    \
     ((V)-(V)+1 << (CHAR_BIT*sizeof(V)-INT_VALUE_SIGNED(V)-1)))

/*
 * Returns minimum value for V's type.

 * Pains are taken (perhaps unnecessarily) to avoid integer overflow
 * with signed types.
 * assert: twos complement architecture
 */
#define INT_VALUE_MIN(V) (-INT_VALUE_MAX(V)-1)

事后思考:如果 V 是一个变量,或者一个包含变量的表达式,这些变量没有被赋值,那么这些调用 UB ......在所问的问题中就是这种情况。它们可能适用于许多实现,但 C 标准并不能保证这一点,并且它们肯定会在使用陷阱值初始化未初始化变量的实现上失败。

【讨论】:

  • +1 用于避免溢出和泛化。 -0.2 用于默认假设没有实现足够疯狂以具有带有填充位的整数类型。不过,四舍五入。
  • INT_VALUE_SIGNED() 通常为以下所有参数返回 1:(signed char)1(unsigned char)1(short)1(unsigned short)1,因为 [unsigned] char/short 升级为 signed int。当unsigned charunsigned short 的大小与int 相同时,它将给出“预期”的结果。否则很好的答案,+1。
  • @AlexeyFrunze 不错。有什么解决办法吗?
  • 我找到了一种用于变量(左值)的方法,但不是用于(r)值的方法。如果您使用右值,我认为您无法在 C99 中解决此问题。看我的回答。
【解决方案4】:

使用 ANSI C 89 可以轻松完成:

    #include<stdio.h>
    #include<limits.h>

int main(void) {

printf("Max value of char: %d\n", CHAR_MAX);
printf("Min value of char: %d\n", CHAR_MIN);

printf("Max value of short: %d\n", SHRT_MAX);
printf("Min value of short: %d\n", SHRT_MIN);

printf("Max value of int: %d\n", INT_MAX);
printf("Min value of int: %d\n", INT_MIN);

printf("\n\n");
return 0;
}

请注意,您可以包含 float.h 然后使用:

printf("Max value of Double: %d\n", DBL_MAX);

虽然不太推荐。

祝你好运, 罗恩

【讨论】:

    【解决方案5】:

    不,标准 C 实现中不存在这样的函数。

    【讨论】:

      【解决方案6】:

      您无法创建执行此操作的函数,但您可以创建一些执行此操作的宏。

      如果你有 C11,你可以使用_Generic:

      #define maxvalue(x) \
        _Generic(x, \
            char: 127, short: 32767, int: INT_MAX, \
            unsigned char: 255, unsigned short: 65535, unsigned int: UINT_MAX)
      

      如果你需要C89,只要你能区分有符号/无符号,你就可以做到:

      #define maxvalue_unsigned(x) ((1<<(8*sizeof(x)))-1)
      #define maxvalue_signed(x) ((1<<((8*sizeof(x)-1)))-1)
      

      如果您愿意要求类型名(或使用 GCC 特定的 typename),您可以使用字符串:

      #define maxvalue_type(x) maxvalue_helper(#x "----------")
      unsigned long long maxvalue_helper(const char *s) {
        switch(*s){
        char 'c': /* char */ return 127;
        char 's': /* short */ return 32767;
        char 'i': /* int */ return INT_MAX;
        /* ... */
        case 'u': /* unsigned */
          switch(9[s]) {
          case 'c': /* unsigned char */ return 255;
          char 's': /* unsigned short */ return 65535;
          char 'i': /* unsigned int */ return UINT_MAX;
          /* ... */
      

      【讨论】:

      • C99 没有_Generic。这是 C11 功能。
      【解决方案7】:

      看起来我已经能够想出一个相对容易使用的宏,SIGNED_VAR(VAR),通过修改、比较和恢复变量的值来测试给定的整数变量是否有符号(所有这些都只是必要的对于小于int) 的类型,同时避免未定义的行为,特别是与有符号溢出和序列点相关的类型。或者看起来是这样。至少,gcc(用-Wall 调用)不会抱怨我在&amp;&amp;|| 运算符之间做疯狂的事情,尽管它不喜欢在三元?: 运算符周围做同样的事情。

      这个宏的好处是它应该与 C89 和 C99 编译器一起使用(1LL 可以替换为 1Llong long 可以替换为 long (和 "%ll" 与 @987654333 @,当然)如果您的 C89 编译器没有来自 C99 的扩展 long long 类型)并且它还正确支持小于 int 的类型(charshort)。

      一旦我们知道变量是否有符号,构造最小值和最大值就很简单了,许多人已经展示了如何做到这一点。宏VAR_MAX()VAR_MIN() 构造这些值并将它们作为最长的C99 整数类型long long 返回。在将无符号值转换为有符号时,我选择返回有符号类型以避免潜在的溢出/UB 问题。由于返回类型long long不能直接将unsigned long long(ULLONG_MAX)的最大值表示为有符号值,如果需要返回该值,则返回-1,在转换为unsigned long long之后将产生ULLONG_MAX。您需要在这里小心一点。

      丑陋的东西来了。希望,我没有错过任何错误。

      哦,当然,有符号类型(例如 min=-128、max=+127)支持 2 的补码的整个不对称范围。

      编辑:我忘了提到SIGNED_VAR() 期望变量被初始化。否则,读取它可能会导致未定义的行为。

      // file: IntVarMinMax.c
      // compile: gcc -Wall -std=c99 -O2 IntVarMinMax.c -o IntVarMinMax.exe
      #include <stdio.h>
      #include <stdlib.h>
      #include <limits.h>
      
      int SignTestTestVal;
      unsigned char SignTestOriginalXchar;
      unsigned short SignTestOriginalXshort;
      
      signed char SignTestRestoreOriginalXchar(void)
      {
        if (SignTestOriginalXchar < SCHAR_MAX + 1u)
          return (signed char)SignTestOriginalXchar;
        return (signed char)(SignTestOriginalXchar - SCHAR_MAX - 1) - SCHAR_MAX - 1;
      }
      
      short SignTestRestoreOriginalXshort(void)
      {
        if (SignTestOriginalXshort < SHRT_MAX + 1u)
          return (short)SignTestOriginalXshort;
        return (short)(SignTestOriginalXshort - SHRT_MAX - 1) - SHRT_MAX - 1;
      }
      
      #define IFELSE(E1,E2,E3) (((E1) && (E2)) || (!(E1) && (E3)))
      #define SEQ(E1,E2) (((E1) && (E2)) || (E2))
      
      #define SIGNED_VAR(VAR)                                     \
      (                                                           \
        IFELSE                                                    \
        (                                                         \
          sizeof(VAR) >= sizeof(int),                             \
          ((VAR) - (VAR) - 1 < 0),                                \
          IFELSE                                                  \
          (                                                       \
            sizeof(VAR) == sizeof(short),                         \
            SEQ(SignTestOriginalXshort = (VAR),                   \
                SEQ(SignTestTestVal = (VAR) = -1,                 \
                    SEQ((VAR) = SignTestRestoreOriginalXshort(),  \
                        SignTestTestVal < 0))),                   \
            IFELSE                                                \
            (                                                     \
              sizeof(VAR) == sizeof(char),                        \
              SEQ(SignTestOriginalXchar = (VAR),                  \
                  SEQ(SignTestTestVal = (VAR) = -1,               \
                      SEQ((VAR) = SignTestRestoreOriginalXchar(), \
                          SignTestTestVal < 0))),                 \
              (fprintf(stderr, "unsupported type!"), exit(-1), 0) \
            )                                                     \
          )                                                       \
        )                                                         \
      )
      
      #define VAR_MAX(SIGNED,VAR)                                     \
      (                                                               \
        SIGNED ?                                                      \
          ((1ll << (sizeof(VAR) * CHAR_BIT - 2)) - 1 +                \
           (1ll << (sizeof(VAR) * CHAR_BIT - 2))) :                   \
          (                                                           \
            (sizeof(VAR) < sizeof(long long)) ?                       \
              ((1ll << (sizeof(VAR) * CHAR_BIT - 1)) - 1 +            \
               (1ll << (sizeof(VAR) * CHAR_BIT - 1))) :               \
              (                                                       \
                (sizeof(VAR) == sizeof(long long)) ?                  \
                  -1ll :                                              \
                  (fprintf(stderr, "unsupported type!"), exit(-1), 0) \
              )                                                       \
          )                                                           \
      )
      
      #define VAR_MIN(SIGNED,VAR)                          \
      (                                                    \
        SIGNED ?                                           \
          (-((1ll << (sizeof(VAR) * CHAR_BIT - 2)) - 1 +   \
             (1ll << (sizeof(VAR) * CHAR_BIT - 2))) - 1) : \
          0                                                \
      )
      
      int main(void)
      {
        signed char sc = 1; char c = 2; unsigned char uc = 3;
        short ss = 4; unsigned short us = 5;
        int si = 6; unsigned int ui = 7;
        long sl = 8; unsigned long ul = 9;
        long long sll = 10; unsigned long long ull = 11;
      
      #define PRINT_VARS()                             \
        printf("sc=%hhd, c=%hhu, uc=%hhu, "            \
               "ss=%hd, us=%hu, si=%d, ui=%u, "        \
               "sl=%ld, ul=%lu, sll=%lld, ull=%llu\n", \
               sc, c, uc,                              \
               ss, us, si, ui,                         \
               sl, ul, sll, ull)
      
      #define TEST_VAR(VAR)                                       \
        {                                                         \
          int varIsSigned = SIGNED_VAR(VAR);                      \
          if (varIsSigned)                                        \
            printf("%lld <= " #VAR " <= %lld\n",                  \
                   VAR_MIN(varIsSigned,VAR),                      \
                   VAR_MAX(varIsSigned,VAR));                     \
          else                                                    \
            printf("%lld <= " #VAR " <= %llu\n",                  \
                   VAR_MIN(varIsSigned,VAR),                      \
                   (unsigned long long)VAR_MAX(varIsSigned,VAR)); \
        }
      
        PRINT_VARS();
      
        TEST_VAR(sc);
        TEST_VAR(c);
        TEST_VAR(uc);
        TEST_VAR(ss);
        TEST_VAR(us);
        TEST_VAR(si);
        TEST_VAR(ui);
        TEST_VAR(sl);
        TEST_VAR(ul);
        TEST_VAR(sll);
        TEST_VAR(ull);
      
        PRINT_VARS();
      
        return 0;
      }
      

      输出(ideone):

      sc=1, c=2, uc=3, ss=4, us=5, si=6, ui=7, sl=8, ul=9, sll=10, ull=11
      -128 <= sc <= 127
      -128 <= c <= 127
      0 <= uc <= 255
      -32768 <= ss <= 32767
      0 <= us <= 65535
      -2147483648 <= si <= 2147483647
      0 <= ui <= 4294967295
      -2147483648 <= sl <= 2147483647
      0 <= ul <= 4294967295
      -9223372036854775808 <= sll <= 9223372036854775807
      0 <= ull <= 18446744073709551615
      sc=1, c=2, uc=3, ss=4, us=5, si=6, ui=7, sl=8, ul=9, sll=10, ull=11
      

      【讨论】:

      • 我什至不……那IFELSE在做什么? C 中有一个三元运算符,你知道的……
      猜你喜欢
      • 2016-10-18
      • 2016-03-28
      • 1970-01-01
      • 2021-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-04
      相关资源
      最近更新 更多