Является ли этот указатель на указатель избыточным?
Код выглядит так:
void insertNode(TreeNode **root, COMPARE compare, void* data) {
TreeNode *node = (TreeNode *)malloc(sizeof(TreeNode));
node->data = data;
node->left = NULL;
node->right = NULL;
if(*root == NULL) {
*root = node;
return;
}
while(1){
if(compare((*root)->data, data) > 0) {
if((*root)->left != NULL) {
*root = (*root)->left;
} else {
(*root)->left = node;
break;
}
} else {
if ((*root)->right != NULL) {
*root = (*root)-> right;
} else {
(*root) -> right = node;
break;
}
}
}
}
Указатель никогда не используется как root
и всегда используется как (*root)
, Разве это не TreeNode **root
лишний? Может ли это быть уменьшено в аргументе TreeNode *root
и части в теле функции изменились на просто root
от (*root)
, Если нет, то почему?
5 ответов
Нет, это не избыточно. C является строгим языком передачи по значению, и, если вы хотите изменить параметр, передаваемый в (и вы доверяете мне), C эмулирует передачу по ссылке с указателями.
В вашем случае вы хотите изменить переменную типа TreeNode *
Таким образом, вам нужно передать указатель на эту переменную, поэтому у вас есть двойная косвенность.
Не обманывайтесь тем фактом, что у вас уже есть указатель, в вашем случае он концептуально такой же, как:
void changeInt (int *pXyzzy) { *pXyzzy = 42; }
кроме того, что для изменения int
, Если вы хотите изменить int *
Вам понадобится:
int some_int = 42;
void changeIntPtr (int **ppXyzzy) { *ppXyzzy = &some_int; }
отмечая дополнительный уровень косвенности. Это больше похоже на то, что вам нужно, так как вы меняете указатель на что-то (поэтому вам нужен двойной указатель на него).
Не проходя в &some_treenode_pointer_variable
и настройку через *some_treenode_pointer_variable = something
измененное значение никогда не возвращается обратно к вызывающей стороне (переменная, передаваемая по значению, является только локальной копией).
Это может помочь изучить следующий код и его вывод. Метод 1 пытается изменить указатель, просто установив его, метод 2 использует метод указателя на указатель, чтобы попытаться изменить его:
#include <stdio.h>
static int someInt;
static void method1 (int *pXyzzy) {
printf ("Pointer on calling method1 = %p\n", pXyzzy);
pXyzzy = &someInt;
printf ("Pointer on exiting method1 = %p\n", pXyzzy);
}
static void method2 (int **ppXyzzy) {
printf ("Pointer on calling method2 = %p\n", *ppXyzzy);
*ppXyzzy = &someInt;
printf ("Pointer on exiting method2 = %p\n", *ppXyzzy);
}
int main (void) {
int *plugh = NULL;
printf ("Pointer in main on start = %p\n", plugh);
method1 (plugh);
printf ("Pointer in main after method1 = %p\n", plugh);
method2 (&plugh);
printf ("Pointer in main after method2 = %p\n", plugh);
return 0;
}
Выход:
Pointer in main on start = 0x0
Pointer on calling method1 = 0x0
Pointer on exiting method1 = 0x404018
Pointer in main after method1 = 0x0
Pointer on calling method2 = 0x0
Pointer on exiting method2 = 0x404018
Pointer in main after method2 = 0x404018
Вы можете видеть, что, хотя значение локальной переменной изменяется в методе 1, это не отражается на вызывающей стороне main
, Способ 2 отражает изменение обратно к вызывающей стороне.
Кроме того, вы не должны бросать возвращаемое значение malloc
в С:
TreeNode *node = (TreeNode *)malloc(sizeof(TreeNode));
Он может скрывать некоторые тонкие ошибки и совершенно не нужен, поскольку C явно void *
к другим типам указателей. Лучше использовать:
TreeNode *node = malloc (sizeof (TreeNode));
Вы также должны проверить возвращаемое значение malloc
чтобы убедиться, что вы не получили NULL
назад, чтобы не было следующей строки:
node->data = data;
не принесет вам конца печали.
Если исходное дерево пусто, корень = NULL.
Когда пользователь звонит
TreeNode* tree = NULL;
insertNode(&tree, compare, data);
Они ожидают, что дерево станет корневым узлом.
Если вы передаете дерево вместо &tree, указатель передается по значению, и у вас нет возможности сделать переменную дерева для указания на вновь созданный узел. (Поскольку вы не можете знать адрес дерева. В функции все, что вы получаете, это NULL, поэтому вы не можете изменить его значение)
if(*root == NULL) {
*root = node;
return;
}
Говорит: если root
указывает на NULL
назначить что-то root
, Если вы получаете TreeNode *p
по параметру и p == NULL
Вы не можете назначить что-либо для p, чтобы это имело влияние на вызывающего.
Эта функция может быть использована с переменной указатель на ноль, и после ее возврата будет иметь действительный TreeNode *
это не указывает на NULL
,
Помните: C получает все значения путем копирования, если вы хотите изменить параметр, вы должны получить указатель на этот параметр. Если вы хотите изменить TreeNode *
Вы должны пройти TreeNode **
Не за что. Линия *root = ...
не может работать, если параметр не был указателем на указатель.
Похоже, что назначение строки "*root = node;" сделать корень * корневым узлом дерева, но на более поздней стадии во время обхода корневой узел изменяется (*root = (*root)-> влево; *root = (*root)-> вправо), что кажется неверным, я считаю временным узел должен использоваться для обхода.