Как отметил Ян ответ , если числа в массиве могут быть как положительными, так и отрицательными и ограничены только ограничениями типа int
, а ненекоторый меньший диапазон, вам нужно вернуть диапазон как целое число без знака, потому что наибольшее целое число минус наименьшее целое переполняет диапазон допустимых целых чисел (но диапазон целых чисел без знака).использовать более длинный тип со знаком, чем тип массива (что делает ответ Яна), но это в конечном итоге приводит к проблемам, когда больше нет типа со знаком.Следующий код доступен в моем репозитории SOQ (Вопросы о переполнении стека) на GitHub в виде файла array-range-17.c
в подкаталоге src / so-5256-3870 .Он использует только типы int
и unsigned
без использования более длинных типов.
/* SO 5256-3870 */
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
static unsigned ArrayRange(size_t num_values, int values[num_values])
{
int large;
int small;
unsigned range;
if (num_values < 1)
return 0;
large = values[0];
small = values[0];
for (size_t i = 1; i < num_values; i++)
{
if (values[i] > large)
large = values[i];
else if (values[i] < small)
small = values[i];
}
if ((large >= 0 && small >= 0) || (large <= 0 && small <= 0))
{
/* Most common cases - both values of same sign; no overflow risk */
/* Any mode of binary arithmetic */
assert(large >= small);
range = large - small;
}
else if ((-INT_MAX == INT_MIN) || (small != INT_MIN))
{
/*
** Two's complement arithmetic with values with different signs
** but the small value is not INT_MIN, or one's complement
** arithmetic or sign-magnitude arithmetic (which have negative
** zeros but no asymmetry in the positive vs negative ranges).
*/
/* Different signs — therefore large is positive and small is negative */
assert(large > 0 && small < 0);
range = (unsigned)large + (unsigned)(-small);
}
else
{
/* Twos-complement arithmetic — small is INT_MIN */
assert(-INT_MAX - 1 == INT_MIN);
assert(small == INT_MIN && large > 0);
range = (unsigned)large + INT_MAX + 1;
}
return range;
}
int main(void)
{
int arrays[][2] =
{
{ +345, +436 },
{ +436, +345 },
{ -199, -439 },
{ -439, -199 },
{ -999, +999 },
{ +999, -999 },
{ 0, 0 },
{ 0, INT_MAX },
{ 0, INT_MIN },
{ 0, INT_MAX },
{ -1000, INT_MAX },
{ +1000, INT_MAX },
{ -1000, INT_MIN },
{ +1000, INT_MIN },
{ INT_MIN, INT_MAX },
{ INT_MIN, INT_MIN },
{ INT_MAX, INT_MAX },
{ INT_MIN, -INT_MAX },
};
enum { NUM_ARRAYS = sizeof(arrays) / sizeof(arrays[0]) };
for (int i = 0; i < NUM_ARRAYS; i++)
{
unsigned int range = ArrayRange(2, arrays[i]);
printf("%+11d:%+-11d = %u\n", arrays[i][0], arrays[i][1], range);
}
return 0;
}
Код тестирования воспроизводит только массивы с двумя элементами, но работает и с массивами большего размера.Вывод:
+345:+436 = 91
+436:+345 = 91
-199:-439 = 240
-439:-199 = 240
-999:+999 = 1998
+999:-999 = 1998
+0:+0 = 0
+0:+2147483647 = 2147483647
+0:-2147483648 = 2147483648
+0:+2147483647 = 2147483647
-1000:+2147483647 = 2147484647
+1000:+2147483647 = 2147482647
-1000:-2147483648 = 2147482648
+1000:-2147483648 = 2147484648
-2147483648:+2147483647 = 4294967295
-2147483648:-2147483648 = 0
+2147483647:+2147483647 = 0
-2147483648:-2147483647 = 1
Как я отмечал в комментариях, если целочисленный тип массива не имеет знака, код очень прост;нет никаких переполнений, о которых следует беспокоиться, и все данные имеют одинаковый признак необходимости.
Вы можете параметризовать приведенный выше код для работы с различными целочисленными типами.Вот код, который может быть скомпилирован с:
-DUSE_INTMAX
- используйте intmax_t
и uintmax_t
. -DUSE_LONG_LONG
- используйте long long
и unsigned long long
. -DUSE_LONG
- используйте long
и unsigned long
. -DUSE_INT64
- используйте int64_t
и uint64_t
. -DUSE_INT32
- используйте int32_t
и uint32_t
. -DUSE_INT16
- используйте int16_t
и uint16_t
. -DUSE_INT
(или ничего из вышеперечисленного) - используйте int
и unsigned
.
Добавление short
, signed char
и int8_t
оставлено как не очень захватывающее упражнение для читателя.
/* SO 5256-3870 */
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
/* Paramterization for signed types */
#if defined(USE_INTMAX)
typedef intmax_t SignedType;
typedef uintmax_t UnsignedType;
#define SIGNED_MAX INTMAX_MAX
#define SIGNED_MIN INTMAX_MIN
#define DATA_FMT "20" PRIdMAX
#define RANGE_FMT PRIuMAX
#elif defined(USE_LONG_LONG)
typedef long long SignedType;
typedef unsigned long long UnsignedType;
#define SIGNED_MAX LLONG_MAX
#define SIGNED_MIN LLONG_MIN
#define DATA_FMT "20lld"
#define RANGE_FMT "llu"
#elif defined(USE_LONG)
typedef long SignedType;
typedef unsigned long UnsignedType;
#define SIGNED_MAX LONG_MAX
#define SIGNED_MIN LONG_MIN
#define DATA_FMT "20ld" // 11ld would do is sizeof(long) == 4
#define RANGE_FMT "lu"
#elif defined(USE_INT64)
typedef int64_t SignedType;
typedef uint64_t UnsignedType;
#define SIGNED_MAX INT64_MAX
#define SIGNED_MIN INT64_MIN
#define DATA_FMT "20" PRId64
#define RANGE_FMT PRIu64
#elif defined(USE_INT32)
typedef int32_t SignedType;
typedef uint32_t UnsignedType;
#define SIGNED_MAX INT32_MAX
#define SIGNED_MIN INT32_MIN
#define DATA_FMT "11" PRId32
#define RANGE_FMT PRIu32
#elif defined(USE_INT16)
typedef int16_t SignedType;
typedef uint16_t UnsignedType;
#define SIGNED_MAX INT16_MAX
#define SIGNED_MIN INT16_MIN
#define DATA_FMT "6" PRId16
#define RANGE_FMT PRIu16
#else /* USE_INT */
typedef int SignedType;
typedef unsigned UnsignedType;
#define SIGNED_MAX INT_MAX
#define SIGNED_MIN INT_MIN
#define DATA_FMT "11d"
#define RANGE_FMT "u"
#endif
static UnsignedType ArrayRange(size_t num_values, SignedType values[num_values])
{
if (num_values == 0)
return 0;
SignedType large = values[0];
SignedType small = values[0];
for (size_t i = 1; i < num_values; i++)
{
if (values[i] > large)
large = values[i];
else if (values[i] < small)
small = values[i];
}
UnsignedType range;
assert(small <= large);
if (small >= 0 || large < 0 || (large == 0 && small != SIGNED_MIN))
{
/* Most common cases - both values of same sign; no overflow risk */
/* Any mode of binary arithmetic */
assert(large >= small);
range = large - small;
}
else if ((-SIGNED_MAX == SIGNED_MIN) || (small != SIGNED_MIN))
{
/*
** Two's complement arithmetic with values with different signs
** but the small value is not SIGNED_MIN, or one's complement
** arithmetic or sign-magnitude arithmetic (which have negative
** zeros but no asymmetry in the positive vs negative ranges).
*/
/* Different signs — therefore large is positive and small is negative */
assert(large > 0 && small < 0);
range = (UnsignedType)large + (UnsignedType)(-small);
}
else
{
/* Twos-complement arithmetic — small is SIGNED_MIN */
assert(-SIGNED_MAX - 1 == SIGNED_MIN);
assert(small == SIGNED_MIN && large >= 0);
range = (UnsignedType)large + SIGNED_MAX + 1;
}
return range;
}
int main(void)
{
SignedType arrays[][2] =
{
{ +345, +436 },
{ +436, +345 },
{ -199, -439 },
{ -439, -199 },
{ -999, +999 },
{ +999, -999 },
{ 0, 0 },
{ 0, SIGNED_MAX },
{ 0, SIGNED_MIN },
{ 0, SIGNED_MAX },
{ -1000, SIGNED_MAX },
{ +1000, SIGNED_MAX },
{ -1000, SIGNED_MIN },
{ +1000, SIGNED_MIN },
{ SIGNED_MIN, SIGNED_MAX },
{ SIGNED_MIN, SIGNED_MIN },
{ SIGNED_MAX, SIGNED_MAX },
{ SIGNED_MIN, -SIGNED_MAX },
};
enum { NUM_ARRAYS = sizeof(arrays) / sizeof(arrays[0]) };
for (int i = 0; i < NUM_ARRAYS; i++)
{
UnsignedType range = ArrayRange(2, arrays[i]);
printf("%+" DATA_FMT ":%+-" DATA_FMT " = %" RANGE_FMT "\n",
arrays[i][0], arrays[i][1], range);
}
return 0;
}
При компиляции с -DUSE_INTMAX
на Mac с MacOS 10.14 Mojave, скомпилированным с GCC 8.2.0, я получаю результат:
+345:+436 = 91
+436:+345 = 91
-199:-439 = 240
-439:-199 = 240
-999:+999 = 1998
+999:-999 = 1998
+0:+0 = 0
+0:+9223372036854775807 = 9223372036854775807
+0:-9223372036854775808 = 9223372036854775808
+0:+9223372036854775807 = 9223372036854775807
-1000:+9223372036854775807 = 9223372036854776807
+1000:+9223372036854775807 = 9223372036854774807
-1000:-9223372036854775808 = 9223372036854774808
+1000:-9223372036854775808 = 9223372036854776808
-9223372036854775808:+9223372036854775807 = 18446744073709551615
-9223372036854775808:-9223372036854775808 = 0
+9223372036854775807:+9223372036854775807 = 0
-9223372036854775808:-9223372036854775807 = 1
Я уверен, что код работает на машине, которая использует арифметику с двумя дополнениями.Я полагаю, что это сработает на машине, которая использует арифметику дополнения или величины знака, но я не могу это проверить.