Luigi D.
Luigi D.

Reputation: 315

Basic issue with C double pointers (matrices)

I am relatively new to programming in general, and I'm trying to write some code to work with square matrices. Unfortunately, I'm stuck very early in the development, as the code

typedef struct Matrix_t{
    float** content;
    size_t size;
} Matrix_t;

int main(int argc, char** argv) {
    Matrix_t* matr;

    initMatrix(matr,s);
    /*
    ...
    ...
    ...
    */
    return 0;
}

void initMatrix(Matrix_t* m, size_t s) {
    int i;

    m = (Matrix_t*) malloc(sizeof(Matrix_t));
    m->content = (float**) malloc(s*sizeof(float*));
    for(i=0;i<s;i++){
        m->content[i] = (float*) malloc(s*sizeof(float));
    }

    m->size = s;
}

would SIGSEGV immediately after initMatrix() is done. Using the debugger, I found out that basically all matrix info is lost after initMatrix() is closed. Why is it? And how can I fix it?

Thanks in advance.

Upvotes: 0

Views: 205

Answers (2)

WhozCraig
WhozCraig

Reputation: 66194

You're modifying only a local automatic variable m in your function. C is a pass-by-value language. If you want to pass by address, then an address is what you have to pass, declaring the formal parameter as a pointer-to-type, even when that type is already a pointer and what you're modifying is the the pointer itself.

When you're passing something as an in/out or out parameter, If you find yourself doing this:

void foo(Type *p)
{
    p = malloc(...)
}

you're not modifying the data pointed to by p, you're modifying p itself, and the caller will be unaware of the change. The address stored in p upon entrance is lost. Where as this:

void foo(Type *p)
{
    *p = ....
}

is modifying what p points to. That said, if Type is already a pointer-type, and what you want to modify it the pointer itself, you must do this:

void foo(Type **pp) // declare a pointer-to-pointer-to-type
{
    *pp = malloc(....) // dereference to store the address returned from malloc
                       // in the pointer pointed to by pp
}

Therefore, the most immediate fix is to declare the formal parameter as a pointer-to-pointer-to-type, passing the address of matr from main()

void initMatrix(Matrix_t **pp, size_t s) 
{
    int i;

    Matrix_t *m = malloc(sizeof(Matrix_t));
    m->content = malloc(s*sizeof(float*));
    for(i=0;i<s;i++)
        m->content[i] = malloc(s*sizeof(float));
    m->size = s;
    *pp = m; // note: saving pointer to the output target
}


int main(int argc, char** argv) 
{
    Matrix_t* matr = NULL;

    initMatrix(&matr,s); // note: passing address of pointer

    /*
    ...
    ...
    ...
    */
    return 0;
}

I left out the error checking (or I should say, I didn't add any, as there was none there to begin with) and removed the unneeded casts from malloc, which you can read more about here.

There are about a half-dozen ways to do this, but this is the most direct to what code you already have. I would advise either returning the allocation from the function itself as the ret-val, or not even dynamic-allocating the structure itself as there is no need to do so. Both of which I leave for you to think about.

Best of luck.

Upvotes: 1

user4459197
user4459197

Reputation: 11

Make matr=NULL in main function and so, try:

main(){

   Matrix_t *matr=NULL;
   /*Declare and initialize "s" variable.... */

   initMatrix(&matr,s);  /* See below...  */
   /*
     .....
   */
}

Use a pointer to pointer in Matrix_t *m in function initMatrix

void initMatrix(Matrix_t **m, size_t s) 
{    
     Matrix_t *aux=(Matrix_t*)malloc(sizeof(Matrix_t));

     /*check if matrix already allocated */  
     if(*m != NULL) 
     {    
         /*Make a free matrix function to free the matrix before use*/          
         free_my_matrix(m); 
         *m=NULL;
     }

     /***
        Work with aux....
        aux->content=...malloc....
     ***/

     /* make your base pointer be aux */           
     *m=aux; 
}

Upvotes: 1

Related Questions