Get the maximum value of a variable in C

前端 未结 7 1771
感动是毒
感动是毒 2021-02-19 21:33

Is there a function in C that returns the maximum value of a variable like this (I will name the function \"maxvalue\" in example below)?

int a;
printf(\"%d\", m         


        
7条回答
  •  [愿得一人]
    2021-02-19 21:42

    Looks like I have been able to come up with a relatively easy-to-use macro, SIGNED_VAR(VAR), to test whether the given integer variable is signed by modifying, comparing and restoring the variable's value (all that is only necessary for types smaller than int) while avoiding undefined behavior, specifically the kinds related to signed overflow and sequence points. Or so it seems. At least, gcc (invoked with -Wall) doesn't complain about me doing crazy things between the && and || operators, although it didn't like the same kind of things around the ternary ?: operator.

    The good thing about this macro is that it should work with C89 and C99 compilers (1LL can be replaced with 1L and long long can be replaced with just long (and "%ll" with "%l", of course) if your C89 compiler does not have the extended long long type from C99) and it also correctly supports types smaller than int (char and short).

    Once we know whether the variable is signed or not, constructing the minimum and maximum values is trivial and many have show how to do it. The macros VAR_MAX() and VAR_MIN() construct these values and return them as the longest C99 integer type, long long. I chose to return the signed type to avoid potential overflow/UB problems when converting unsigned values to signed. Since the returned type long long cannot represent the maximum value of unsigned long long (ULLONG_MAX) directly as a signed value, if that value needs to be returned, a -1 is returned instead, which after a cast to unsigned long long will produce ULLONG_MAX. You need to be a little careful here.

    Here goes the ugliness. Hope, I didn't miss a bug.

    Oh, and, of course, it's expected that the entire asymmetric range of 2's complement values is supported in signed types (e.g. min=-128, max=+127).

    EDIT: I've forgot to mention that SIGNED_VAR() expects the variable to be initialized. Otherwise, reading it can result in undefined behavior.

    // file: IntVarMinMax.c
    // compile: gcc -Wall -std=c99 -O2 IntVarMinMax.c -o IntVarMinMax.exe
    #include 
    #include 
    #include 
    
    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;
    }
    

    Output (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
    

提交回复
热议问题