Получить максимальное значение переменной в C

Есть ли функция в C, которая возвращает максимальное значение переменной, подобной этой (я буду называть функцию "maxvalue" в примере ниже)?

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

Таким образом, в принципе функция возвращает значения, такие как INT_MAX, когда переменная подписана INT, UINT_MAX, когда unsigned int и т.д.

Ответ 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

Ответ 2

Вы можете сделать это довольно легко с помощью типового выражения типа C11:

#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-разрядная машина.

Ответ 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 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 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)

Afterthought: они вызывают UB, если V - переменная или выражение, содержащее переменные, которым не присвоено значение... что имеет место в вопросе, заданном пользователем. Они, вероятно, будут работать во многих реализациях, но стандарт C не гарантирует этого, и они, безусловно, потерпят неудачу при реализации, которая инициализирует неинициализированные переменные со значениями ловушки.

Ответ 4

Нет, такая функция не существует в стандартной реализации C.

Ответ 5

Вы не можете создать функцию, которая делает это, но вы можете создать некоторые макросы, которые это делают.

Если у вас есть 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, вы можете сделать это, если можете различать подписанный /unsigned:

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

Если вы захотите потребовать имя типа (или используйте GCC-specific 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;
    /* ... */

Ответ 6

Похоже, что мне удалось создать относительно простой в использовании макрос SIGNED_VAR(VAR), чтобы проверить, подписана ли данная целочисленная переменная, изменяя, сравнивая и восстанавливая значение переменной (все, что только необходимо для типов, меньших, чем int), избегая при этом поведения undefined, в частности видов, связанных с подписанными точками переполнения и последовательности. Или так кажется. По крайней мере, gcc (вызванный с помощью -Wall) не жалуется на то, что я делаю сумасшедшие вещи между операторами && и ||, хотя это не понравилось тем же самым вещам вокруг тройного оператора ?:.

Хорошо, что этот макрос должен работать с компиляторами C89 и C99 (1LL может быть заменен на 1L и long long можно заменить просто long"%ll" на "%l", конечно), если ваш компилятор C89 не имеет расширенного типа long long от C99), и он также правильно поддерживает типы, меньшие, чем int (char и short).

Как только мы узнаем, подписана ли переменная или нет, построение минимального и максимального значений тривиально, и многие из них показывают, как это сделать. Макросы 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).

EDIT. Я забыл упомянуть, что SIGNED_VAR() ожидает, что переменная будет инициализирована. В противном случае чтение этого файла может привести к поведению undefined.

// 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

Ответ 7

Легко можно сделать, используя 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);

Хотя, это менее рекомендуется.

Удачи, Рон