Как ссылка на указатель точно работает в C ++ и когда они нам нужны (в случае связанного списка) - PullRequest
0 голосов
/ 27 января 2020

Я знаю, что указатели содержат адрес переменной. И ссылки указывают на один и тот же адрес в таблице символов (то есть тот же адрес переменной, которой они назначены).

Мой вопрос: как именно работают ссылки на указатели? И когда они нам нужны, в отличие от использования только указателя (и не использования ссылки на указатель). Было бы полезно, если бы вы могли объяснить мне использование ссылки на указатель в отношении односвязного списка.

У меня есть следующий код, который удаляет указатель заголовка связанного списка с помощью функции:

struct Node
{
    int data;
    Node* next;
};

struct Node* newNode(int data)
{
    Node* temp = new Node;
    temp->data = data;
    temp->next = nullptr;
    return temp;
}

Node* deleteHead(Node* &head)
{
    if (head)
    {
        Node* temp = head;
        head = head->next;
        delete temp;
    }
    return head;
}

int main()
{
    Node* head = newNode(1);
    head->next = newNode(6);
    head->next->next = newNode(4);
    head->next->next->next = newNode(8);

    head = deleteHead(head);
    Node* temp = head;
    while (temp != nullptr)
    {
        cout << temp->data << " " << endl;
        temp = temp->next;
    }
    return 0;
}

В функции deleteHead(Node* &head) функция принимает аргумент Node* &head. Но код работает нормально, даже если аргумент Node* head. для каких случаев нам нужно передать Node* & вместо Node* в связанном списке?

Ниже приведена функция deleteHead(Node* &head), описанная выше, которая работает так же, если мы используем Node* head только как аргумент вместо Node* &head -

Function that works the same with Node* and Node* &

Ответы [ 4 ]

1 голос
/ 27 января 2020

Со ссылкой на функцию указателя deleteHead вы можете использовать head = deleteHead (head) или deleteHead (head), так как head является просто ссылкой на head в основной функции, любое изменение заголовка в функции deleteHead фактически применяется к переменная head в основной функции. В версии с указателем вы должны использовать head = deleteHead (head).

1 голос
/ 27 января 2020

Вы передаете указатель по ссылке по той же причине, по которой вы передаете не указатель по ссылке: чтобы функция могла изменять свое значение.

Позвольте мне использовать более простой пример

#include <iostream>

void foo(int*& x) {
    *x = 42;        // change the value of the int x points to
    x = nullptr;    // change the value of x
}

Первая строка изменяет значение, на которое указывает x (но не изменяет x). Вторая строка изменяет саму x.

int main() {
    int y = 42;
    int* y_ptr = &y;
    foo(y_ptr);
    if (y_ptr == &y) std::cout << "cannot happen";
}

Поскольку мы установили x = nullptr, y_ptr больше не будет указывать на y после вызова.

Теперь, если мы изменим foo, чтобы не брать ссылку, мы get:

#include <iostream>

void foo(int* x) {
    *x = 42;        // change the value of the int x points to
    x = nullptr;    // change the value of x
}

Снова первая строка изменяет int, на который указывает x. Однако теперь вторая строка влияет только на x локально для функции.

int main() {
    int y = 42;
    int* y_ptr = &y;
    foo(y_ptr);
    if (y_ptr == nullptr) std::cout << "cannot happen";
}

Значение y_ptr нельзя изменить, передав его foo, поскольку оно передается по значению.

В вашем коде у вас есть

Node* deleteHead(Node* &head)
{
    if (head)
    {
        Node* temp = head;
        head = head->next;
        delete temp;
    }
    return head;
}

И когда вы пишете head = deleteNode(head), происходят две вещи:

  • функция изменяет head (потому что она передается по ссылке), указывая на head->next.
  • функция также возвращает эту "новую" головку (указывающую на head->next), которая назначается на head.

Таким образом, вы в основном назначаете head дважды. Поскольку head передается по ссылке, deleteNode будет делать правильные вещи без использования возвращаемого значения:

deleteNode(head);  // this already does modify head 

... или наоборот: если вы возвращаете "новую" голову ( head->next) из функции и присвойте ее head, тогда не имеет значения, если вы передадите указатель по ссылке, потому что присвоение, выполненное внутри функции, имеет тот же эффект.

Ваш код похож на

int* bar(int*& x) {
   x = nullptr;
   return x;
}

и затем вызовите его через

int y = 42;
int* y_ptr = &y;
y_ptr = bar(y_ptr);

, где тот же эффект может быть достигнут без использования возвращенного значения bar(y_ptr). Или то же самое без указателей (потому что указатели здесь действительно не имеют значения):

int moo(int& x) {
    x = 0;
    return x;
}

int x = 42;
x = moo(x);     // same as `moo(x)`

PS: вам не нужны оба (вернуть указатель и назначить его уже в функции), поэтому лучше заставить функцию возвращаться void.

1 голос
/ 27 января 2020

Ссылки - это «безопасные указатели», которые используются с семантикой значений (что действительно полезно в контексте перегрузки операторов), поэтому использование ссылок очень похоже на использование указателей в C, за исключением следующих пунктов:

  1. Ссылки содержат одно значение, а не массив
  2. Ссылки не являются нулевыми (что не всегда желательно)

Это означает, что вы можете ( или, должны) передавать ссылку всякий раз, когда вы хотите изменить исходную переданную переменную (а не ее копию).

Сказав это, C (rought) эквивалент вашей функции равен Node* deleteHead(Node** head). Обратите внимание, что, поскольку вы передали ссылку, исходная переменная head была изменена, и, таким образом, ваша функция становится немного странной, так как она модифицирует head и возвращает свое значение. Вы можете использовать один из следующих параметров:

(1) удаляет заголовок (если размер списка не пуст) и возвращает указатель на следующий элемент, это не рекомендуется, так как он оставит заголовок как свисающий указатель. Это ваша исходная функция, но она не получает ссылку.

Node* deleteHead(Node* head)
{
    if (head)
    {
        Node* temp = head; // You might want to use auto
        head = head->next;
        delete head;
    }
    return head;
}

(2) То же, что и ваша функция, но не возвращает значения (потому что вы уже изменили голову). Этот не будет работать без передачи ссылки.

void deleteHead(Node* &head)
{
    if (head)
    {
        Node* temp = head->next; 
        delete head; // deletes the content of head
        head = temp;
    }
}
1 голос
/ 27 января 2020

Итак,

и ссылка, и указатель содержат адрес переменной / памяти. ссылка имеет семантию c переменной (если вы установите значение, вы записываете данные в ссылочную память), указатель имеет семантиу c указателя (если вы установите значение, указанная память не изменяется), и вы можете установить указатель, который обращается к другой памяти.

о deleteHead (Node * & head) - вы используете ссылку на реальную переменную, которая содержит указатель Node. Функция возвращает новое значение head в той же переменной и в качестве возвращаемого значения.

...