#c #scope #pass-by-reference #pass-by-value
#c #область видимости #передача по ссылке #передача по значению
Вопрос:
Создавая очень простой связанный список, я обнаружил, что запутался в том, что, вероятно, является очень простой концепцией области видимости. Первый работает так, как ожидалось. Казалось бы, во второй функции makeNode узел «n» выходит из области видимости при завершении. Я в замешательстве, почему это происходит.
Я почти уверен, что выделенная память все еще там, и оба метода имеют указатель на эту память. Итак, почему один из них не работает?
программа имеет две функции make node
#include <iostream>
using namespace std;
struct Node
{
int value;
Node* next;
Node() : next(NULL){}
};
Node* makeNode1(int value)
{
Node* n = new Node;
n->value = value;
return n;
}
void makeNode2(int value, Node* mountPt)
{
Node* n = new Node;
n->value = value;
mountPt = n;
}
void destroyNode(Node* mountPt)
{
if(mountPt->next != NULL)
{
destroyNode(mountPt->next);
delete mountPt;
}
}
int main() {
Node* origin = NULL;
// This works
origin = makeNode1(4);
// causes a runtime error when dereferenced
makeNode2(4, origin);
return 0;
}
Комментарии:
1. Откройте главу в вашей книге по C , в которой объясняется разница между передачей параметров функции по значению и передачей их по ссылке, и прочитайте ее. Вы передаете его по значению и
makeNode2
утечка памяти. Указатель, переданный в is, не инициализируется, и он остается неинициализированным в вызывающем объекте, потому что он был передан по значению. Вот как работает C .2. Произошла утечка
makeNode2
, вам следует добавить строкуif(mountPt) delete mountPt;
, чтобы сначала убедиться, что предыдущее выделение удалено, а затем его можно назначитьn
.3. Vuwox верен.
4. @Amadeus Да, я понимаю, что вышеупомянутая компиляция, но без удаления, эта функция создает утечку памяти. И, добавляя его, компилятор не будет выдавать
parameter 'mountPt' set but not used
будет использовать полный уровень предупреждений, а также ошибка времени выполнения также не будет добавляться.
Ответ №1:
Поскольку makeNode2
параметр указателя mountPt
передается по самому значению, то любая модификация самого указателя внутри функции like mountPt = n;
не имеет ничего общего с исходным аргументом origin
.
Вы можете изменить его на передачу по ссылке, т.Е.
void makeNode2(int value, Node*amp; mountPt)
Комментарии:
1. Обратите внимание, что это приводит к утечке памяти; исходный
origin
указатель, выделенный вmakeNode1
, заменяется новым, выделенным вmakeNode2
безdelete
редактирования.2. Я в курсе. Очевидно, что требуется только одна функция. Я просто показывал обе конфигурации.
Ответ №2:
В первом случае вы создаете новый раздел памяти с указателем на этот раздел памяти (который является просто адресом), а затем возвращаете этот адрес. Второй пример передает указатель на адрес в памяти, а затем переназначает этот адрес (т. Е. изменяет это значение) в области видимости функции. Однако значение этого указателя (адрес, на который он ссылается) возвращается к своему предыдущему вызову после завершения функции, поскольку указатель уже имеет значение в стеке. Разница в том, что при первом вызове вы изменяете значение, сохраненное в адресе, который в конечном итоге передается из функции, в то время как при втором вы меняете адрес, на который указывает указатель, но этот указатель возвращается к указанию на старый адрес, как только вы меняете область видимости обратно на основной текст.
Комментарии:
1. Передача указателя по значению все равно позволит вам навсегда изменить значение, хранящееся по адресу, но не позволит вам изменить то, на что указывает указатель.
2. «возвращается назад» — плохой способ описать это. Источник вообще никогда не меняется.
Ответ №3:
Измените функцию для следующего:
void makeNode2(int value, Node* mountPt)
{
Node* n = new Node;
n->value = value;
if(mountPt) delete mountPt;
mountPt = n;
}
где, если mountPt уже был выделен, он будет освобожден, а затем origin
может указывать на выделение n
Комментарии:
1. Я согласен, что это было бы лучшим управлением памятью, но не будет ли это иметь ту же проблему с областью видимости?
2. @mreff555 Да, это было бы.
3. На самом деле это не так. Но если к параметру Node * добавлен оператор ссылки, как упоминалось выше, это работает
Ответ №4:
Решение X-Y: устраните необходимость в makeNode
функции, расширив конструктор Node
struct Node
{
int value;
Node* next;
Node(int val = 0) :value(val), next(NULL){}
};
Использование
Node * origin = new Node(4);
Ответ №5:
Хотя все вышеперечисленное, включая принятый ответ, имеет отношение к делу, ни одно из них напрямую не указывает на фактический логический недостаток. Вместо mountPt = n;
вам нужно mountPt->next = n;
.