Нужна помощь в отладке - PullRequest
       8

Нужна помощь в отладке

0 голосов
/ 29 сентября 2018

Функция, которую я пытаюсь создать, называется:

int Range(int values[], int numValues)

Цель - вернуть разницу между наибольшим значением в массиве и наименьшим значением в массиве.

Ниже приведен мой код, однако он не работает.Любая помощь будет оценена :) 1006

#include <stdio.h>

int Range(int values[], int numValues)
{
    int i;
    int large;
    int small;
    int displayone;
    int displaytwo;
    int calc;

    large = values[0];
    small = values[0];

    for(i=0;i<numValues;i++){

        if(values[i]>large){
            displayone = values[i];
        }
        else if(values[i] < small){
            displaytwo = values[i];
        }
    }

    calc = displayone - displaytwo;

    return calc;
}

Ответы [ 2 ]

0 голосов
/ 16 октября 2018

Как отметил Ян ответ , если числа в массиве могут быть как положительными, так и отрицательными и ограничены только ограничениями типа 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

Я уверен, что код работает на машине, которая использует арифметику с двумя дополнениями.Я полагаю, что это сработает на машине, которая использует арифметику дополнения или величины знака, но я не могу это проверить.

0 голосов
/ 29 сентября 2018

Вам необходимо правильно отслеживать малые и большие значения.Вы можете удалить переменные displayone и displaytwo, так как они не нужны.Просто используйте large и small для отслеживания значений в цикле.

Строго говоря, чтобы сделать функцию устойчивой, вы также должны рассмотреть несколько других вещей:

  1. Случай, когда количество элементов равно нулю (или меньше) - В этом случае диапазон будет неопределенным.Возвращение нуля, вероятно, лучшее, что мы можем сделать.
  2. Что результат вычитания одного int значения из другого может привести к переполнению - Например, рассмотрим INT_MAX - INT_MIN.Результат этого, около 4 миллиардов, намного больше, чем примерно 2 миллиарда, которые может представить без знака int.Математически диапазон всегда будет положительным числом;отрицательный, вероятно, здесь не имеет смысла.Таким образом, создание возвращаемого типа unsigned int должно быть уместным и предотвращать переполнение, а также обеспечить расширение промежуточных переменных large и small до long long, чтобы обеспечить правильное окончательное вычитание.

Со всеми вышеизложенными соображениями функция становится:

#include <stdio.h>

unsigned int Range(int values[] , int numValues)
{
    int i;
    long long large;               // <<<< Make long long to support ...
    long long small;               // <<<< ... final subtraction
    unsigned int calc;             // <<<< Make unsigned int to match return

    if (numValues < 1)             // <<<< Handle invalid input
        return 0;

    large = values[0];
    small = values[0];

    for(i=0;i<numValues;i++){

        if(values[i]>large)  
            large = values[i];      // <<<< assign to large here
        else if(values[i] < small)
            small = values[i];      // <<<< and similar for small
    }

    calc = (uint)(large - small);           // <<<< then calculate difference

    return calc;    
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...