2015-03-12 2 views
0

Я не могу понять смысл кода C о связанных списках, в которых используются двойные указатели. Вот код, который я читаюC двойной указатель Значение

struct list 
{ 
    int value; 
    struct list *next; 
}; 
//Insert an element at the begining of the linked list 
void insertBegin(struct list **L, int val) 
{ 
    //What does **L mean? 
    //Memory allocation for the new element temp 
    struct list *temp; 
    temp = (struct list *)malloc(sizeof(temp)); 
    //The new element temp points towards the begining of the linked list L 
    temp->next = *L; 
    //Set the beginning of the linked list 
    *L = temp; 
    (*L)->value = val; 
} 
void loop(struct list *L) 
{ 
    printf("Loop\n"); 
    //Run through all elements of the list and print them 
    while(L != NULL) 
    { 
     printf("%d\n", L->value); 
     L = L->next; 
    } 
} 
struct list* searchElement(struct list *L,int elem) 
{ 
    while(L != NULL) 
    { 
     if(L->value == elem) 
     { 
      printf("Yes\n"); 
      return L->next; 
     } 
     L = L->next; 
    } 
    printf("No\n"); 
    return NULL; 
} 

int main() 
{ 
    struct list *L = NULL; 
    insertBegin(&L,10); // Why do I need 
    return 0; 
} 

Что **L в insertElement значит и в чем разница между **L и *L в функции loop? Почему в основном, когда объявлено struct list *L = NULL, я должен вызвать функцию insertBegin с аргументом &L, а не простым L?

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

Благодарим за помощь!

+0

http://stackoverflow.com/a/897414/2318775 – avim

+0

'temp = (struct list *) malloc (sizeof (temp));' -> 'temp = malloc (sizeof * temp);' – joop

ответ

0

Если вы хотите, функция записи в параметр и есть что новое значение отражается в вызывающем, то вы должны передать указатель для этого параметра:

void foo(T *p) // for any type T 
{ 
    *p = new_value(); // write a new value to the thing p points to 
} 

void bar(void) 
{ 
    T var; 
    foo(&var); // foo writes a new value to var 
} 

Если подставить T с типом указателя Q * , то код

void foo(Q **p) // for any type Q 
{ 
    *p = new_value(); // write a new value to what p points to 
} 

void bar(void) 
{ 
    Q *var; 
    foo(&var); // foo writes a new value to var 
} 

Семантика в обоих случаях точно такая же; мы хотим, чтобы foo обновил значение, хранящееся в var, с помощью указателя p. Единственное различие заключается в том, что во втором случае var уже имеет тип указателя, поэтому p должен быть указателем на этот тип указателя.

В коде, который вы отправили, функция insertBegin обновляет значение, хранящееся в L, что является указателем на заголовок списка. Так как переменная L в основном имеет тип struct list *, то параметр L в insertBegin должен быть struct list **.

1

Тип **L читается как указатель на указатель на L. Так что если у вас есть указатель на L и занимает его адрес, это то, что вы получаете. Образец **L в аргументе функции (в C) обычно используется для реализации «выходного параметра» - параметра, который код может обновить. Чтобы вставить в начале, вам нужно обновить указатель на голову списка - поэтому эта функция принимает указатель на голову в качестве параметра. При назначении *L функция обновляет параметр.

+0

Что означает L L? К списку списка? Если да, мне кажется, что вам не нужно использовать указатель ** L, вы можете напрямую изменить адрес, содержащийся в указателе * L. – George

+0

Место памяти, в котором может быть указатель на L. В основном он имеет адрес переменной или члена указателя типа на L. – DrC

0

Двойной указатель в insertBegin предназначен для тех случаев, когда вы меняете местоположение L, где L всегда находится на узле, который вы вставляете. При вызове функции вам нужно & L, потому что вам необходимо передать ее по ссылке, потому что вы меняете L

1

Это означает «указатель на указатель». В C указатели передаются по значению, поэтому, если вы хотите иметь возможность изменять указатель, переданный функции вне этой функции, вам нужно передать указатель на него. Передача указателя будет передавать только другое значение для него, которое будет изменено в функции, но не будет отражать изменение значения вне его. Передача в качестве указателя на указатель по существу позволяет вам изменять значение на адресе, а не просто изменять локальный.

Рассмотрим эти две функции:

void foo(int * p, int * t) { 
    p = t; 
} 

void foo2(int ** p, int * t) { 
    *p = t; 
} 

И код:

int a = 1, b = 2; 
int * p = &a; 

foo(p, &b); 
// in foo *p == 2, but here *p is still == 1, because p didn't change, only the copy that was passed to foo changed 
foo2(&p, &b); // now *p == 2, p was changed in foo2 
1

L хранит адрес первой ссылки в списке. Таким образом: * L является содержанием первой ссылки в списке, и & L является адреса переменной, которая хранит адрес первой ссылки в списке.

Другими словами, ваш единственный способ выделить память и инициализировать список, передав аргумент в функцию, является предоставление & L в качестве аргумента. Если вы передадите L в качестве аргумента, функция получит адрес первой ссылки, тогда как вместо этого ему нужно место, где магазин адрес первой ссылки.

+0

Итак, вы хотите изменить адрес первого узла, другими словами * L. Чтобы изменить * L, вам нужно использовать указатель на * L. Вот почему мы должны использовать ** L? Я прав? – George

+0

Да, у вас это получилось :) – user2551017

Смежные вопросы