nuclear
nuclear

Reputation: 105

Modify pointer of pointer of struct

I create a struct, pass the pointer of pointer into the function to then call malloc(). That all works fine. However if I try to access the memory, the program just freezes. If I call another function and change access the memory then everything works fine.

void test(TFeld *Feld, TEinstellung E)
{
int i;

    for (i=0;i<E.Groesse_X*E.Groesse_Y;i++)
    {
        Feld[i].Schiff_Vorhanden = false;
        Feld[i].Schiff_Versunken = false;
        Feld[i].Ueberprueft = false;
    }   
}

void initField (TEinstellung E, TFeld **Feld)
{
    int i;

    *Feld = (TFeld*)malloc(E.Groesse_X*E.Groesse_Y*sizeof(TFeld));

    test(*Feld,E);

    /*  for (i=0;i<E.Groesse_X*E.Groesse_Y;i++)
    {
        Feld[i]->Schiff_Versunken = (bool*)false;
    //  (*Feld[i]).Schiff_Versunken = false;
        //Feld[i]->Ueberprueft = false;
    } */
}

with the definiton of TFeld:

typedef struct TFeld
{
    bool Schiff_Vorhanden = false;
    bool Ueberprueft = false;
    bool Schiff_Versunken = false;
} TFeld;

The part I commented out just crashed the program while using the test function works.

Can someone please explain me that behavior.

Upvotes: 2

Views: 183

Answers (3)

Christophe
Christophe

Reputation: 73366

Whats the problem ?

In InitField() the parameter Feld is declared as a pointer to a pointer to TFeld.

*Feld is hence a pointer to TFeld. It is correctly initialized to a freshly allocated memory region of the correct size.

You then call test() passing *Feld as argument. Unfortunately you call the argument Feld as well, so that the type of these variable is different and could cause some headaches. But this is not the problem. The test function should do what yo expect.

When you're back in InitField() you then try to access to the elements that you have initialized:

Feld[i]->Schiff_Versunken = ... //ouch !!!

This takes the pointer to pointer and access the ith pointer in that table. But as your pointer of pointer is just a poitner and not an array, you get back a pointer which is completely corrupt. You then dereference this rogue pointer with -> pretending that it points to a TFeld. When you then assign a valu into this rogue address you have undefined behavior (could be segfault, could be freeze, could be anything).

Edit: additional information on pointer dereferencing:

The operators * -> and [] have an order of precendence which you have to get used to. Let's look at the TFeld **Feld :

  • *Feld[i] is the same that *(Feld[i]) because first [] is applied and then only *. By the way, going further, and applying pointer arithmetic rules, it's the same as *(*(Feld+i)). In any case, it's not what yo want
  • *Feld[i].xxx is the same as *((Feld[i]).xxx) because . has a higher precedence than *. This will not compile because Feld[i] is not of type TFeld
  • Feld[i]->xxx is the same as (Feld[i])->xxx (because [] and -> have the same precedence but when both appear they are applied from left to right). This is further the same as (*(Feld[i])).xxx. And still not what you want.

But lets visualize to understand the impact of applying dereferencing in a different order. On top is what you need. The botom is what you should avoid:

enter image description here

How to solve ?

I suggest to change the commented out part into:

for (i=0;i<E.Groesse_X*E.Groesse_Y;i++)
{
    (*Feld)[i].Schiff_Versunken = false;
    (*Feld)[i].Schiff_Versunken = false;
    (*Feld)[i].Ueberprueft = false;
} 

Or if you don't like stars and partenheses:

    Feld[0][i].Schiff_Versunken = false;
    Feld[0][i].Schiff_Versunken = false;
    Feld[0][i].Ueberprueft = false;

DOuble indirections are allways a little tricky. Whenever you're in doubt, add some parentheses.

A final word: I assumed here that InitField() was called with a valid pointer to a pointer to TFeld, so that *Feld would without a doubt point to a pointer to TFeld. If this would be the case, the pointer of you memory allocation could be written anywhere in memory, causing again memory corruption. If in doubt, make an edit tou your question showing the calling code so that I can check.

Upvotes: 4

Twinkle
Twinkle

Reputation: 524

There is difference when you do it inside a function and directly . Because here Feld is a pointer to structure in test(), whereas it is a pointer to the pointer to the structure in initField().

When you used malloc(), the pointer to the structure got initialised, hence it didnt crashed when accessed inside test(). But the pointer to the pointer is still uninitialized, hence caused the seg fault in initField().

So you have to allocate the double pointer correctly. Check this link for reference : C - dynamic memory allocation using double pointer

Upvotes: 0

Dan R
Dan R

Reputation: 1494

Feld is a pointer to an array of structs. So you have to (1) dereference the pointer, then (2) index into the array, and finally (3) access the field inside the struct.

There are two ways to write this within your for loop; which one you like better is a question of taste:

(*Feld)[i].Schiff_Versunken = false;  // option 1
(*Feld + i)->Schiff_Versunken = false; // option 2

Upvotes: 1

Related Questions