Является ли этот указатель на указатель избыточным?

Код выглядит так:

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)-> вправо), что кажется неверным, я считаю временным узел должен использоваться для обхода.

Другие вопросы по тегам