Массивы переменного размера и указатель массива в C - PullRequest
0 голосов
/ 18 апреля 2019

Я занимаюсь программированием в HackerRank. В упражнении C ++ с переменными размерами я пытался сделать это с C, но не смог сделать это правильно.

Мой код иногда проходит простой тестовый пример 0, но иногда нет. Код был точно таким же, но получил другой ответ.

int main() {
    /* Enter your code here. Read input from STDIN. Print output to STDOUT */

    int n, q;
    scanf("%d%d", &n, &q);

    int *p_arr[n];

    if (n > 0) {
        for (int i = 0; i <  n; i++) {
            int tmp;
            scanf("%d", &tmp);

            int tmp_arr[tmp];
            p_arr[i] = tmp_arr;
            for (int j = 0; j < tmp; j++) {
                int value;
                scanf("%d", &value);
                p_arr[i][j] = value;
                printf("%d ", p_arr[i][j]);
            }
            printf("\n");
        }
    }

    if (q > 0) {
        for (int i = 0; i < q; i++) {
            int row, col;
            scanf("%d%d", &row, &col);
            printf ("%d %d\n", row, col);
            int answer = p_arr[row][col];
            printf("%d\n", answer);

        }
    }
    return 0;

}

Это был неправильный ответ, который я получил

1 5 4 

1 2 8 9 3 

0 1

21973

1 3

32764

Я не знал, откуда взялись 21973 и 32764.

Это был ответ, который я ожидал

1 5 4 

1 2 8 9 3 

0 1

5

1 3

9

Иногда я получал неправильный ответ, иногда я получал правильный. Почему это было? Большое спасибо!

1 Ответ

0 голосов
/ 18 апреля 2019

Продолжая комментарии, если в упражнении используется массив переменной длины, его следует записать на C, поскольку стандарт C ++ не предоставляет VLA (и они были сделаны необязательными, начиная с C11 после добавления с C99)

Основная проблема с вашим кодом (за исключением полного сбоя при распределении входных данных) заключается в том, что он вызывает неопределенное поведение , поскольку int tmp_arr[tmp] выходит из области видимости в конце цикла for. То, что вы назначаете с помощью p_arr[i] = tmp_arr;, больше не существует вне цикла, потому что tmp_arr больше не существует. Любая попытка получить доступ к значению после его выхода из области действия вызывает неопределенное поведение , см. неопределенное, неопределенное и определяемое реализацией поведение

Каждый раз, когда вы обрабатываете ввод, вы должны проверять , ввод успешен, и полученное значение находится в допустимом диапазоне. Например, n и q должны быть положительными значениями. Используя scanf вы можете сделать (минимально)

    if (scanf("%d%d", &n, &q) != 2 || n <= 0 || q <= 0) {
        fputs ("error: invalid format or value.\n", stderr);
        return 1;
    }

Вышеприведенное подтверждает, что 2 входа были получены и оба являются положительными значениями.

Чтобы сохранить указатель, назначенный с помощью p_arr[i] = tmp_arr;, tmp_arr должен быть выделенным типом для обработки неизвестного числа tmp элементов, или он должен быть объявлен вне цикла (и достаточно большим, чтобы обрабатывать все ожидаемые значения). Значения - которые учитывая, что tmp читается как ввод - не похоже на предполагаемый подход). Вы также не можете объявить tmp_arr статическим, поскольку будет только один экземпляр, и при многократном присвоении ему p_arr[i] все элементы p_arr[i] будут указывать на одно и то же место.

Просто выделите tmp_arr, и тогда присвоение p_arr[i] сохранится в течение всей жизни программы или до ее освобождения, например,

        int *tmp_arr = calloc (tmp, sizeof *tmp_arr);   /* allocate */
        if (!tmp_arr) {                 /* validate every allocation */
            perror ("calloc-tmp_arr");
            return 1;
        }
        p_arr[i] = tmp_arr;

( примечание: calloc использовалось выше для обнуления новой выделенной памяти. Вместо этого можно использовать malloc, поскольку код присваивается каждому элементу)

Сложив все это и добавив (минимальную) проверку, вы можете сделать что-то похожее на:

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

int main (void) {

    int n, q;   /* validate EVERY input */
    if (scanf("%d%d", &n, &q) != 2 || n <= 0 || q <= 0) {
        fputs ("error: invalid format or value.\n", stderr);
        return 1;
    }

    int *p_arr[n];  /* array of n-pointers to int */

    for (int i = 0; i < n; i++) {
        int tmp;
        if (scanf("%d", &tmp) != 1 || tmp <= 0) {   /* validate! */
            fputs ("error: invalid input - tmp.\n", stderr);
            return 1;
        }

        int *tmp_arr = calloc (tmp, sizeof *tmp_arr);   /* allocate */
        if (!tmp_arr) {                 /* validate every allocation */
            perror ("calloc-tmp_arr");
            return 1;
        }
        p_arr[i] = tmp_arr;
        for (int j = 0; j < tmp; j++) {
            int value;
            if (scanf("%d", &value) != 1) {     /* validate! */
                fputs ("error: invalid input - value.\n", stderr);
                return 1;
            }
            p_arr[i][j] = value;
            printf ("%d ", p_arr[i][j]);
        }
        putchar ('\n');     /* no need to printf a single-character */
    }

    for (int i = 0; i < q; i++) {
        int row, col;   /* validate! */
        if (scanf ("%d%d", &row, &col) != 2 || row < 0 || col < 0) {
            fputs ("error: invalid input, row or col.\n", stderr);
            return 1;
        }
        printf ("%d %d\n%d\n", row, col, p_arr[row][col]);
    }

    for (int i = 0; i < n; i++)
        free (p_arr[i]);

    return 0;
}

( Примечание: не проверено, поскольку ввод хакерранка не был предоставлен)

Посмотрите вещи и дайте мне знать, если у вас есть дополнительные вопросы.

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