0

I am doing coding exercise in HackerRank. In the Variable Sized Arrays exercise of C++, I tried to do it with C, but I couldn't get it right.

My code sometimes pass the simple test case 0, but sometimes not. The code was exactly the same but came out with different answer.

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;

}

This was the wrong answer I got

1 5 4 

1 2 8 9 3 

0 1

21973

1 3

32764

I didn't know where the 21973 and 32764 came from.

This was the answer I expected

1 5 4 

1 2 8 9 3 

0 1

5

1 3

9

Sometimes I got the wrong answer, sometimes I got the correct. Why was that? Thanks a lot!

bereal
  • 32,519
  • 6
  • 58
  • 104
keung
  • 3
  • 1
  • 6
    `Variable Sized Arrays exercise of C++` is an oxymoron, C++ doesn't provides "Variable Length Arrays" except through non-standard compiler extensions. – David C. Rankin Apr 18 '19 at 06:52
  • 1
    I think it would help if you shared the exercise description as well. – bereal Apr 18 '19 at 06:54
  • 1
    `int tmp_arr[tmp]` goes out of scope at the end of the `for` loop. What you assign to `p_arr[i] = tmp_arr;` no longer exists outside the loop. – David C. Rankin Apr 18 '19 at 06:54
  • 1
    `int tmp_arr[tmp];` is an array on the stack, its lifetime ends with its scope, so it is gone in the next iteration and accessing it in the next for loop with `int answer = p_arr[row][col];` is undefined behavior. Change it to `int *tmp_arr = malloc(tmp * sizeof *tmp_arr);` – mch Apr 18 '19 at 06:54
  • You need to do `scanf("%d%d", &n, &q); if(n <= 0) halt_and_catch_fire(); int *p_arr[n];`. You can't declare an array with size zero in C, that's undefined behavior. – Lundin Apr 18 '19 at 06:56
  • 1
    `"Sometimes I got the wrong answer, sometimes I got the correct. Why was that?"` See: [Undefined, unspecified and implementation-defined behavior](https://stackoverflow.com/questions/2397984/undefined-unspecified-and-implementation-defined-behavior) – David C. Rankin Apr 18 '19 at 07:00
  • Yes, correct that is what I meant. Sorry for the confusion. It was the assignment to `p_arr[i]` that would ultimately be the issue. Do you not see `int answer = p_arr[row][col];` in the `for (int i = 0; i < q; i++)`? – David C. Rankin Apr 18 '19 at 07:32
  • It is used in the `for` loop following `if (q > 0)` which is in an entirely different scope. – David C. Rankin Apr 18 '19 at 07:37

1 Answers1

0

Continuing from the comments, if the exercise involves a Variable Length Array, then it should be written in C because the C++ standard does not provide VLAs (and they have been made optional beginning in C11 after being added with C99)

The primary problem with your code (aside from a complete failure to allocate the inputs) is that it invokes Undefined Behavior because int tmp_arr[tmp] goes out of scope at the end of the for loop. What you assign with p_arr[i] = tmp_arr; no longer exists outside the loop because tmp_arr no longer exists. Any attempt to access a value after it has gone out of scope invokes Undefined Behavior, see Undefined, unspecified and implementation-defined behavior

Every time you handle input, you must validate the input succeeded and the value obtained is within a valid range. For instance both n and q must be positive values. Using scanf you could do (minimally)

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

The above validates that 2 inputs were received and that both are positive values.

In order to preserve the pointer assigned with p_arr[i] = tmp_arr;, tmp_arr must be an allocated type to handle an unknown tmp number of elements, or it must be declared outside the loop (and large enough to handle all anticipated values -- which given that tmp is read as input -- doesn't seem like the intended approach). You cannot declare tmp_arr static either as there would only be one instance and assigning it repeatedly to p_arr[i] would leave all elements of p_arr[i] pointing to the same place.

Simply allocate for tmp_arr and then the assignment to p_arr[i] will survive for the life of the program or until it is freed, e.g.

        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;

(note: calloc was used above to zero the new memory allocated. You can use malloc instead since the code assigns to each element)

Putting it altogether and adding (minimal) validation, you could do something similar to:

#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;
}

(Note: untested as no hackerrank input was provided)

Look things over and let me know if you have further questions.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85