2016-07-04 4 views
2

Я создаю структуру, передаю указатель указателя в функцию, чтобы затем позвонить malloc(). Все работает отлично. Однако, если я пытаюсь получить доступ к памяти, программа просто зависает. Если я вызываю другую функцию и меняю доступ к памяти, тогда все работает нормально.Изменить указатель указателя на структуру

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; 
    } */ 
} 

с Definiton из TFeld:

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

Часть I закомментирована просто разбило программу, используя функцию работы теста.

Может кто-нибудь, пожалуйста, объясните мне это поведение.

+3

Где вы написали 'Feld [i] ->' вы, вероятно, имели в виду '(* Feld) [i] .' – immibis

+0

В вашем' typedef '' struct' вы можете оставить имя 'TFeld 'в первый раз у вас есть. Как в, 'typedef struct {...} TFeld;' –

+0

bool pointer ?! : D –

ответ

4

В чем проблема?

В InitField() параметр Feld указан как указатель на указатель на TFeld.

*Feld, следовательно, указатель на TFeld. Он правильно инициализируется свежей выделенной областью памяти правильного размера.

Затем вы звоните test() проходя *Feld в качестве аргумента. К сожалению, вы также вызываете аргумент Feld, так что тип этой переменной отличается и может вызвать некоторые головные боли. Но это не проблема. Функция тестирования должна делать то, что ожидаете.

Когда вы вернулись в InitField() вы попробуете получить доступ к элементам, которые вы инициализированы:

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

Это принимает указатель на указатель и доступ к указатель-ю в этой таблице. Но поскольку ваш указатель на указатель - это просто poitner, а не массив, вы возвращаете указатель, который полностью поврежден. Затем вы разыгрываете этот указатель-поводырь с ->, делая вид, что он указывает на TFeld. Когда вы затем назначаете ценность этому жульническому адресу, у вас есть неопределенное поведение (может быть segfault, может быть заморожено, может быть что угодно).

Edit: дополнительная информация по указателю разыменования:

Операторы *-> и [] имеют order of precendence, которые вы должны привыкнуть. Давайте посмотрим на TFeld **Feld:

  • *Feld[i] тот же, что *(Feld[i]), потому что первый [], а затем применяется только *. Кстати, идя дальше и применяя правила арифметики указателей, это то же самое, что и *(*(Feld+i)). В любом случае, это не то, что вам нужно
  • *Feld[i].xxx такое же, как *((Feld[i]).xxx), потому что . имеет более высокий приоритет, чем *.Это не будет компилироваться, так Feld[i] не тип TFeld
  • Feld[i]->xxx таким же, как (Feld[i])->xxx (потому что [] и -> имеют одинаковый приоритет, но когда оба появляются они применяются слева направо). Это то же самое, что и (*(Feld[i])).xxx. И еще не то, что вы хотите.

Но давайте визуализируем, чтобы понять влияние применения разыменования в другом порядке. Наверху то, что вам нужно. Ботом это то, что вам следует избегать:

enter image description here

Как решить?

Я предлагаю изменить закомментирована часть в:

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; 
} 

Или, если вам не нравятся звезды и partenheses:

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

DOUBLE indirections являются ВСЕГДА немного сложнее. Всякий раз, когда вы сомневаетесь, добавьте некоторые круглые скобки.

Заключительное слово: я предположил, что здесь InitField() был вызван с действительным указателем на указатель на TFeld, так что *Feld бы без точки сомнения в указатель на TFeld. Если это будет так, указатель на выделение памяти может быть записан в любом месте в памяти, что приведет к повторному повреждению памяти. Если вы сомневаетесь, сделайте для редактирования свой вопрос, показывающий код вызова, чтобы я мог проверить.

+0

ok, поэтому я предполагаю, что это называется (* Feld [i]). Schiff_Versunken = false; равен * Feld [i] -> Schiff_Versunken = false; правильно? Потому что именно это я не понял сначала (* Feld) [i] .Schiff_Versunken = false; отличается от (* Feld [i]). Schiff_Versunken = false; – nuclear

+0

@ ядерный Я понимаю ваше замешательство. См. Мое редактирование о порядке приоритета.Я также добавил схему, чтобы показать различное значение '(* Feld) [i]' и '* Feld [i]' – Christophe

1

Feld - указатель на массив структур. Таким образом, вы должны (1) разыменовать указатель, затем (2) индекс в массив и, наконец, (3) получить доступ к полю внутри структуры.

Существует два способа написать это в вашей петле for; который вам нравится лучше, это вопрос вкуса:

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

Существует разница, когда вы делаете это внутри функции и непосредственно. Потому что здесь Feld является указателем на структуру в test(), тогда как это указатель на указатель на структуру в initField().

Когда вы использовали malloc(), указатель на структуру получил инициализацию, поэтому он не разбился при доступе внутри test(). Но указатель на указатель все еще не инициализирован, поэтому вызвал ошибку seg в initField().

Значит, вам нужно правильно назначить двойной указатель. Проверьте эту ссылку для справки: C - dynamic memory allocation using double pointer