0

The question might appear incredibly simple but how do I declare a function in c that returns a double array. For instance

char myfunction(int a, int b)
{
...
char d[a][b];
...
return d;
}

But it doesn't seem to work (if the response could avoid the 'malloc' and 'free' usage, it could be more helpful to understand those kinds of basic usage)

Best, Newben

epsilones
  • 11,279
  • 21
  • 61
  • 85
  • Unrelated to the declaration of the function, but you're allocating `d` on the stack and returning that value, which is undefined behavior. See [this classic post](http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope) for more details. Instead, you need to `malloc` the memory in your function and then `free` it in the caller. If you want to temporarily work around the problem of dynamic memory allocation while you're learning, make `d` a global or file-scope variable - you can still return it. – Kevin Vermeer Jul 23 '12 at 19:59
  • 1
    I have something that seems to work [here](http://ideone.com/dPLd9). Be wary, though, as there's no `free` and I'm not 100% sure if it's totally correct apart from that issue. – chris Jul 23 '12 at 20:03
  • Ok I am going to try it. So there's no way to avoid the malloc process – epsilones Jul 23 '12 at 20:06
  • @Newben: Not necessarily true, but if we're going to continue to work under the assumption that returning a 2D "array" from a function is an actualy requirement, and that the array cannot be statically allocated, then yes, you're correct. – Ed S. Jul 23 '12 at 20:09

4 Answers4

3

But it doesn't seem to work (if the response could avoid the 'malloc' and 'free' usage, it could be more helpful to understand those kinds of basic usage)

Well, you can't because you are proposing to return a pointer which is invalid once the function returns. The array is declared with automatic storage duration and, as a result, all references to it are invalid once the function returns. You cannot return arrays from functions in C.

If you need to populate data in the function then either return a char** that you malloc from within the function.

char **foo(int cols, int rows) {
  char **ret = malloc(rows * sizeof(char*));
  if(!ret)
    return ret;

  for(int i = 0; i < rows; ++i) {
    ret[i] = malloc(cols);
    /* error checking again for malloc,
       call free() on pointers 0...i upon failure.
       memset to 0 if you like */
  }

  return ret;
}

You can now use multi-dimensional array syntax on the return value.

char **p = foo(10, 20);

// p[j] returns a pointer to char, p[j][k] returns a char
char elem = p[j][k];
Ed S.
  • 122,712
  • 22
  • 185
  • 265
1

In this situation you can't avoid malloc and free. The problem is by declaring

char d[a][b];

You're allocating an array directly on the stack, so when your function returns the array is de-allocated and the address you returned is invalid.

Using something more like:

char** myfunction(int a, int b) {
    char** d = (char**)malloc(a * sizeof(char*));

    if (d ! = NULL) {
        for (int i = 0; i < b; i++) {
            d[i] = (char*)malloc(sizeof(char));

            if (d[i] == NULL) {
                for (int j = 0; j < i; j++) {
                    free(d[j]);
                }

                free(d);
                d = NULL;

                break;
            }
        }
    }

    return d;
}
AlcoJaguar
  • 411
  • 4
  • 13
  • 1
    No need to cast the return value of malloc in C, and it can actually lead to subtle bugs. A `void*` can be implicitly and safely converted to any type of pointer in C. – Ed S. Jul 23 '12 at 20:11
  • @EdS. Yeah, there's no explicit need, just kind of longhand that's kind of a habit for me. What sort of bugs do you mean? – AlcoJaguar Jul 23 '12 at 20:18
  • 1
    On older compilers (this includes all versions of VS) it can hide the fact that you forgot to include `stdlib.h`, so `malloc` is assumed to be an extern function which returns `int`. Without the cast a warning will be thrown, with it nothing will. It is also redundant, not idiomatic, and does nothing but add clutter to C code. – Ed S. Jul 23 '12 at 20:32
1

In C, if you want a function to create an array and return it to its caller, you must use malloc or another function that dynamically allocates memory (such as calloc or realloc). You can then return a pointer to the array.

char **make2dArray(int x, int y) {
    int i;
    char **array = calloc(x, sizeof(char*));
    for(i = 0; i < x; i++) {
        array[i] = calloc(y, sizeof(char));
    }
    return array;
}

If, on the other hand, you simply need to modify the value of an array, it is best to take the array as a parameter and modify it in place.

void modify2dArray(int x, int y, char **array) {
    int i, j;
    for(i = 0; i < x; i++) {
        for(j = 0; j < y; j++) {
            array[i][j] = 1;
        }
    }
}

And remember, once you're done with dynamically allocated memory, call free for each allocation:

void dealloc2dArray(int x, int y, char **array) {
    int i;
    for(i = 0; i < x; i++) {
        free(array[i]);
    }
    free(array);
}

Here's what using these functions might look like:

char **array = NULL;
array = make2dArray(5,6);
modify2dArray(5,6,array);
dealloc2dArray(5,6);
array = NULL;

Please note that the dynamic allocation functions (malloc, calloc, and realloc) can fail, returning NULL. I didn't check for NULL after each allocation here to save on space, but it's generally good practice to ensure that your allocations succeed.

Eric Finn
  • 8,629
  • 3
  • 33
  • 42
1

In C since C99 the correct way to allocate a real 2D array, and not an emulation as in the other answers, is to do something like the following:

char (*array)[cols] = malloc(cols * rows);

If you have to do that in a function, the easiest way for me seems to be

void * foo(int cols, int rows) {
  char (*ret)[cols] = malloc(rows * cols);
  // do something with it
  return ret;
}

When calling it you'd have to be a bit more careful to give the dimension to the new variable

char (*array)[cols] = foo(cols, rows);

All of this has the advantage that one simple malloc is used to allocate a contiguous object, and that you only need to do one call to free at the end to deallocate the memory.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177