-1

Suppose you have a function in C that accepts the dimensions for a 2d array (for simplicity's sake, say for a square nxn array), dynamically allocates the array, then returns it.

I'm aware allocating memory here might be considered somewhat bad practice to begin with, since it will need to be freed elsewhere, but suppose that's not a huge issue. I'm wondering if there's any advantages/disadvantages associated with these two variations of said function:

Variation 1 - Locally define int** variable in function, allocate/return array:

int **create_array(int n) {
    // define array pointer, allocate array...
    int **a_ = (int**)calloc(n,sizeof(int*));
    for (int i = 0; i < n; i++) 
        a_[i] = (int*)calloc(n,sizeof(int));
    return a_;
}

int main() {
    int n = 3;
    int **array2d = create_array(n)
    printf("First element: %d%c",array2d[0][0],'\n');
    // do stuff... etc...
}

Variation 2 - Add in-out int** parameter to function, allocate/return array:

int **create_array_2(int **a_, int n) {
    // allocate array...
    a_ = (int**)calloc(n,sizeof(int*));
    for (int i = 0; i < n; i++) 
        a_[i] = (int*)calloc(n,sizeof(int));
    return a_;
}

int main() {
    int n = 3;
    int **array2d;
    array2d = create_array_2(array2d,n);
    printf("First element: %d%c",array2d[0][0],'\n');
    // do other stuff... etc...
}

Obviously they return the same result and achieve the same task, but is one considered to be safer/more efficient/better practice than the other? In my opinion the 2nd variation just makes things look a bit redundant, but I'm curious if there's any real differences between the two and what happens on the stack/heap when they're called. Hopefully this isn't a dumb question; it's just something I've been curious about. If anyone has insight to share, I'd appreciate it.

d0rz
  • 1
  • 1
  • 2
    Variation 2 makes no sense. You are not using the value of parameter passed to the function, you are just assigning a new value to it right away. There are no "in-out parameters" in C by the way. You can pass a pointer to emulate pass-by-reference, but that's not what's going on in Version 2, and you don't need it anyway. Just use version 1. – n. m. could be an AI May 31 '16 at 21:24
  • 3
    There is no 2D array in the code shown and nothing which can point to one. A pointer is not an array! – too honest for this site May 31 '16 at 21:47
  • `int **a` is not a 2D its a pointer lookup table. – Michi May 31 '16 at 23:08
  • Sorry, poor wording. I referred to them as 2D arrays because they're allocated to be indexed as such. And thanks, @n.m. – d0rz May 31 '16 at 23:44
  • I'm so sick of pedants who don't understand what "array" means in programming, or wilfully pretend not to – M.M Jun 01 '16 at 02:28

2 Answers2

0

I'll probably try to avoid calling malloc and free to many times so this kind of approach is what I'll do:

Example 1:

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

int *foo(size_t row, size_t col);

int main(void){
    int *arr;
    unsigned int row, col, k;

    printf("Give the ROW: ");
    if ( scanf("%u",&row) != 1){
        printf("Error, scanf ROW\n");
        exit(1);
    }

    printf("Give the COL: ");
    if ( scanf("%u",&col) != 1){
        printf("Error, scanf COL\n");
        exit(2);
    }

    arr = foo(row, col);
    for (k = 0 ; k < (row * col) ; k++){
        printf("%d ",arr[k]);
    }

    free(arr);
}

int *foo(size_t row, size_t col){
    unsigned int i, j;
    int *arr = malloc(sizeof *arr * row * col);
    int l = 0;

    if(arr == NULL){
        printf("Error, malloc\n");
        exit(3);
    }

    for ( i = 0; i < row ; i++){
        for ( j = 0 ; j < col ; j++){
            arr[i * col + j] = l;
            l++;
        }
    }

    return arr;
}

Output:

Give the ROW: 6
Give the COL: 3
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

Example 2 (if you are working with the standard C):

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

int (*foo(size_t row, size_t col))[];

int main(void){
    size_t row, col;


    printf("Give the ROW: ");
    if ( scanf("%zu",&row) != 1){
        printf("Error, scanf ROW\n");
        exit(1);
    }

    printf("Give the COL: ");
    if ( scanf("%zu",&col) != 1){
        printf("Error, scanf COL\n");
        exit(2);
    }

    int (*arr)[col] = foo(row, col);

    for ( size_t i = 0; i < row; i++){
        for( size_t j = 0; j < col; j++){
            printf("%d ",*(*(arr+i)+j));
        }
    }

    free(arr);
}

int (*foo(size_t row, size_t col))[]{
    int (*arr)[col] = malloc(row * col * sizeof(int));
    int l=0;

    if (arr == NULL){
        printf("Error, malloc\n");
        exit(3);
    }

    for ( size_t i = 0; i < row; i++){
        for( size_t j = 0; j < col; j++){
            *(*(arr+i)+j) = l;
            l++;
        }
    }

    return arr;
}

Output:

Give the ROW: 6
Give the COL: 3
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

The whole point here is that the call of malloc and free in both examples takes place only one time

Michi
  • 5,175
  • 7
  • 33
  • 58
  • Which part of any C standard requires a "kernel" for implementations, or that the kernel be "disturbed" by malloc? – autistic May 31 '16 at 22:56
-2

IMO, of the two you are better off simply returning the value. This way there's a pure and solid wall between you and the caller.

"Give me some stuff!"

"Okay, here's some stuff."

On the other hand, for actually allocating an array of fixed size, why bother with pointers? Why not declare your return type so as to be castable to a sized array?

int (*p2a)[15] = (int(*)[15])create_array_2(15, 15);

Then you would calloc(15*15,sizeof(int)) and be done.

Community
  • 1
  • 1
aghast
  • 14,785
  • 3
  • 24
  • 56
  • 1
    This is so very wrong! I don't even mean the practical problem with allocating a large array on the stack, or the unnecessary, thus potentially dangerous cast. Or assigning a pointer to an array. – too honest for this site May 31 '16 at 21:48
  • Yeh. When you wrote "Why bother with pointers?" did you intend to imply that your example below *doesn't* use pointers? I suppose `calloc` returns *burgers* instead? Did you mean `p2a` to be a pneumonic for "pointer to array"? Because it isn't; what you have declared is an *array of pointers*... Please learn C before you attempt to teach it. Perhaps you meant `int (*p2a)[15]`... – autistic May 31 '16 at 23:05
  • @Seb I think what you meant to say was "Hey, you forgot a pair of parens." Thanks for that. Fixed. As for the why bother with pointers, better to let the compiler do simple math than have add a layer of extra pointers to indirect through. :-) – aghast May 31 '16 at 23:16
  • This code is incorrect if used with OP's `create_array_2`. If you actually meant to use a different allocation function then you should show that in your answer – M.M Jun 01 '16 at 02:29