Tandura
Tandura

Reputation: 908

Generic bidimensional array

I want to create a bidimensional array like so:

void **mdeclaraMatrice(int nrLini,int nrColoane, int sizeOfElement)
{
    int i;
    void **m = malloc(nrLini * 4);
    if(m==NULL)
        return NULL;
    for(i=0; i<nrLini; i++)
    {
        *(m + (i*4)) = malloc(nrColoane * sizeOfElement);
        if(*(m + (i*4)) == NULL)
             return NULL;
    }
    return m;
}

I whant to use it like this:

int **m = (int **)mdeclaraMatrice(n,m,sizeof(int));

but it doesn't work. What do I do wrong?

Upvotes: 7

Views: 535

Answers (5)

Actually, you don't even need your generic 2D array function if you know the powerfull VLA features of C99. To allocate a true 2D array (no index array required), you just do this:

int (*twoDIntArray)[width] = malloc(height*sizeof(*twoDIntArray));

That's it. Accesses are just as simple:

twoDIntArray[line][column] = 42;

In this code, twoDIntArray is a pointer to an array of width integers. The malloc() call simply allocates enough space for height such line arrays. When you do the pointer arithmetic twoDIntArray[line], you add the size of line line arrays to the pointer, which produces the address of the corresponding line array. This line array is then indexed by the second array subscript [column].

Needless to say that freeing such an array is just as trivial:

free(twoDIntArray);

Upvotes: 2

barak manos
barak manos

Reputation: 30136

You should use m[i] instead of *(m+i*4) and let the compiler do the arithmetic.

In addition, you should deallocate the already-allocated memory in case of a failure.

Try this instead:

void **mdeclaraMatrice(int nrLini, int nrColoane, int sizeOfElement)
{
    int i;
    void **m = malloc(nrLini * sizeof(void*));
    if (m == NULL)
        return NULL;
    for (i=0; i<nrLini; i++)
    {
        m[i] = malloc(nrColoane * sizeOfElement);
        if (m[i] == NULL)
        {
             while (i-- > 0)
                 free(m[i]);
             free(m);
             return NULL;
        }
    }
    return m;
}

Upvotes: 5

alk
alk

Reputation: 70971

[not an answer to the question, but to the indented usage of the proper answer as given by others]

To access the void pointer array as an array of int, doing this

int **m = (int **)mdeclaraMatrice(n,m,sizeof(int));

is not correct, as per the C-Standard only void* converts to any other pointer properly, void** doesn't necessarily. So it shall correctly be

void ** ppv = mdeclaraMatrice(n,m,sizeof(int));
int * pi = *ppv;  /* Please note, there is NO casting necessary here! */

Then access the members like so:

pi[0] = 42  
pi[1] = 43; 
...

Which essently is the same as doing

*((int *) (pi + 0)) = 42;
*((int *) (pi + 1)) = 43;

which indeed does not make sense really as pi already is int*, so the fully correct approach (also taking into account the 2nd dimension) would be:

((int *)(ppv[0]))[0] = 42;
((int *)(ppv[0]))[1] = 43;

Which could be made usable by definging a macro:

#define GENERIC_ARRAY_ELEMENT(type, address, r, c) \
  ((type *)(address[r]))[c] 

GENERIC_ARRAY_ELEMENT(int, ppv, 0, 0) = 42;
GENERIC_ARRAY_ELEMENT(int, ppv, 0, 1) = 43;

Upvotes: 4

2501
2501

Reputation: 25753

I will address the problem of allocation an array of void pointers and then interpreting them as an array of int pointers.

int **nope = (int **)mdeclaraMatrice(n,m,sizeof(int));

Even assuming the allocation was completely correct the assignment and later usage of nope is undefined behavior. void** and int** have incompatible types.

What you can do is the following. Assign the void pointers one by one to an array of int pointers.

void** arrp = mdeclaraMatrice(n,m,sizeof(int));
int* arr[n] ;
for( size_t i = 0 , i < n ; i++ )
    arr[i] = arrp[i] ;

And then use the arr array, When you want to free the memory you free the original pointer:

free( arrp ) ;

Upvotes: 3

Philipp Murry
Philipp Murry

Reputation: 1682

The problem occurs in this line:

*(m + (i*4)) = malloc(nrColoane * sizeOfElement);

You have to know that when adding a number to an address, the address will be incremented by the number times the size of the object the address points to. So if your pointer points to an object that is of size 4 bytes, and you add 1 to it, then the address will automatically be incremented by 4, not by 1. So you should abandon *4.

Also, use the sizeof operator when allocating space, because addresses (and thus pointers) can have different sizes on different processor architectures.

Upvotes: 2

Related Questions