0

I was training C on this kata: https://www.codewars.com/kata/54d7660d2daf68c619000d95/c and got stuck by a strange signature of the function:
long long (*get_res(long long lst[][2], int row, long long LCD))[2]

Here is how I create and malloc my array:

long long (*get_res(long long lst[][2], int row, long long LCD))[2]
{
    long long **res;
    long long mul = 0;

    res = (long long **)malloc(sizeof(long long *) * row);
    if (!res)
        return (NULL);
    for (int i = 0; i < row; i++)
    {
        res[i] = (long long *)malloc(sizeof(long long) * 2);
        mul = LCD / lst[i][1];
        res[i][0] = lst[i][0] * mul;
        res[i][1] = lst[i][1] * mul;
    }
    return ((long long(*)[2])res);
}

Then I return the result of this function in the main function like this:

return (get_res(lst, row, LCD));

And I get some strange results on the tests. I've already checked all the calculations: they work just as expected. Here's how the tester decided to get the result of the function (yet another point that I don't understand):

    long long data[3][2] = { {1, 2}, {1, 3}, {1, 4} };
    dotest(data, 3, "{{6, 12},{4, 12},{3, 12}}");
    ...
    void dotest(long long a[][2], int row, char* sexpr) 
    {    
    long long* act = convertFrac(a, row);
    char* sdat = array2D2StringInt(a, row, 2);
    char* sact = array2D2StringInt((long long(*)[2])act, row, 2);
    //some checking
    }

So here are the questions:
1. What should I do to make the program work as expected?
2. Why would you opt for this signature instead of a classic type **Func_name(params) ?
  • 2
    By "non-traditional" do you really mean "unnecessarily confusing"? – tadman May 28 '21 at 12:41
  • 1
    Tip: Instead of `long long` just say what you mean, like `int64_t`. – tadman May 28 '21 at 12:41
  • @tadman yup, that's what I mean – Igor Petrov May 28 '21 at 12:57
  • The main problem is that you lie to the compiler and tell it you'll return a pointer to a 2D array. But you malloc a fragmented look-up table thing, which is not a 2D array. See [Correctly allocating multi-dimensional arrays](https://stackoverflow.com/questions/42094465/correctly-allocating-multi-dimensional-arrays). – Lundin May 28 '21 at 13:06

1 Answers1

4

You choose to return a pointer to long long[2], so you should allocate an array of long long[2], not long long*.

Also note that casting results of malloc() family is considered as a bad practice.

long long (*get_res(long long lst[][2], int row, long long LCD))[2]
{
    long long (*res)[2];
    long long mul = 0;

    res = malloc(sizeof(*res) * row);
    if (!res)
        return (NULL);
    for (int i = 0; i < row; i++)
    {
        mul = LCD / lst[i][1];
        res[i][0] = lst[i][0] * mul;
        res[i][1] = lst[i][1] * mul;
    }
    return res;
}
MikeCAT
  • 73,922
  • 11
  • 45
  • 70
  • Thank you for your answer! But I don't get one thing: do we still allocate the whole array on the heap? Because this half-static declaration makes me think that we don't... – Igor Petrov May 28 '21 at 12:56
  • @IgorPetrov What `malloc()` gets is an integer to determine the size to allocate. It won't care how the integer is calculated and return some resion on the heap (if successful). – MikeCAT May 28 '21 at 12:57
  • `res = malloc( sizeof(long long[row][2]) );` might be easier to read. And returning the array by parameter might be easier to read too: `void get_res (int row, long long (**lst)[row][2]) { ... *lst = res; }` – Lundin May 28 '21 at 13:11