Двойной указатель: указатель на член структуры, который является указателем
Я пытаюсь написать программу для воспроизведения "Pangolin" (как этот парень - он задает вопросы "да / нет", идет по двоичному дереву, пока не доберется до листового узла. Затем он "угадывает", и если пользователь говорит ответ был неверным, спрашивает пользователя, о чем он думает, и отвечает на вопрос, который отличает это от неправильных подсказок. Затем он добавляет новые данные в дерево).
Это моя структура для узла дерева. NodeType - это QUESTION_NODE для узлов, содержащих вопрос, или OBJECT_NODE для узлов, содержащих "объект" - это то, о чем программа выводит пользователя, о котором он думает. Узлы вопросов имеют указатели на дочерние узлы - один для да и один для нет.
typedef struct _TreeNode {
NodeType type;
union {
char* question;
char* objectName;
} nodeString;
//children for yes and no answers: will be invalid when type is OBJECT_NODE
struct _TreeNode* yes;
struct _TreeNode* no;
} TreeNode;
Поскольку это учебное упражнение, я пытаюсь сделать это с помощью двойных указателей. Вот функция, которая должна добавить узел вопроса в дерево:
void addData(TreeNode** replace, char* wrongGuess) {
//create a new object node for what the user was thinking of
// ... (code to get user input and build the new object node struct) ... //
//create a new question node so we don't suck at pangolin so much
// ... (code to get a question from the user and put it in a question node struct) ... //
//link the question node up to its yes and no
printf("What is the answer for %s?\n", newObjectName);
if (userSaysYes()) {
newQuestionNodePtr->yes = newObjectNodePtr;
newQuestionNodePtr->no = *replace;
}
else {
newQuestionNodePtr->no = newObjectNodePtr;
newQuestionNodePtr->yes = *replace;
}
//redirect the arc that brought us to lose to the new question
*replace = newQuestionNodePtr;
}
Функция addData затем вызывается так:
void ask(node) {
//(... ask the question contained by "node" ...)//
//get a pointer to the pointer that points to the yes/no member pointer
TreeNode** answerP2p;
answerP2p = userSaysYes() ? &(node.yes) : &(node.no);
//(... the user reports that the answer we guessed was wrong ...)//
puts("I am defeated!");
//if wrong, pass the pointer to pointer
addData(answerP2p, answerNode.nodeString.objectName);
Мое (предположительно неправильное) понимание таково:
В "ask()" я передаю addData указатель, который указывает на члена "узла" "да" (или нет). Этот член в свою очередь является указателем. Когда в addData я назначаю "*replace", это должно изменить структуру, перенаправив указатель на член "yes" (или no), чтобы он указывал на новый узел вопроса, который я создал.
Я отладил и обнаружил, что newQuestionNode и newObjectNode созданы успешно. Дочерние элементы newQuestionNode назначены правильно. Однако новый узел вопроса не вставляется в дерево. Строка "*replace = newQuestionNodePtr" не имеет ожидаемого эффекта, а узел, на который ссылается "узел" в области видимости "ask", не перенаправляет свой дочерний указатель.
Кто-нибудь может увидеть, что не так в моем понимании? Или, может быть, я не выразил это прямо в своем коде? Извините, этот вопрос так долго.
2 ответа
Вы не должны объявлять указатель, который вы передаете функции, как двойной указатель. Вместо этого передайте адрес одного указателя на функцию:
TreeNode* answerP2p;
answerP2p = userSaysYes() ? node.yes : node.no;
addData(&answerP2p, answerNode.nodeString.objectName);
К сожалению, я не совсем понимаю ответ Йоахима Пилеборга выше, но в конце концов я подозревал свою проблему и думаю, что это довольно распространенная ошибка для новых путешественников C [1], поэтому я опубликую ее здесь на своих собственных условиях.
В моем поспешном переходе от Java к C I я сказал себе: "Хорошо, структуры - это просто объекты без методов". Оценка обоснованности этого упрощения оставлена читателю в качестве упражнения. Я также расширил это предположение до "когда аргумент имеет структурный тип, он автоматически передается по ссылке". Это очевидно ложно, но я даже не думал об этом. Глупый.
Так что настоящая проблема здесь в том, что я проходил мимо ask()
переменная типа TreeNode
для его node
аргумент. Вся эта структура передавалась по значению (конечно). Когда я прошел answerP2p
в addData()
, это на самом деле работает правильно, но это было изменение ask()
локальная копия TreeNode
, Я изменился ask()
взять TreeNode*
и вот, там было дерево.
- C чем я там занимался [1]?