功能声明或原型。我该如何定义它?

时间:2013-09-29 05:51:28

标签: c++ c function linked-list

我最近一直在研究链表和树。但我不确定何时声明一个函数:

preorder(struct node* root); 
or
preorder(struct node** root);
当两者的工作完全相同时。更确切地说,我必须将我的函数设计为双指针和单指针。

感谢。

P.S:在链表中插入节点需要有双指针,如:

insert(struct node** root,int value);

除非将根节点定义为全局值。虽然预订顺序适用于单个指针。如果有人能够以此为例进行解释,那将非常有帮助。

5 个答案:

答案 0 :(得分:5)

这取决于preorder()的作用。如果它在不修改列表/树的情况下打印一些内容,则只需要一个指针。如果有可能必须替换根,则需要双指针。

这是因为参数是按C中的值传递的。如果原始变量的副本传递给您的函数,则无法修改原始变量:

int inc(int x)
{
    x = x + 1; // this will never work
}

要解决这个问题,不要传入参数,而是可以传入该参数的地址(指向它的指针)。然后,该函数可以取消引用指针并修改它指向的值。

// usage: inc(&x) where x is an int
int inc(int *ptr)
{
    *ptr = *ptr + 1; // this will work
}

使用列表/树,您已经在使用指针。这允许您访问和修改指向对象(例如,获取/设置根的next成员),但不允许您修改指针本身(例如,用不同的节点替换根)。为此,需要引入另一个级别,因此指向节点的指针。

答案 1 :(得分:2)

preorder(struct node** root);

在这里传递root的地址,因为您可能希望使用该功能更新它。

preorder(struct node* root);

在这里,您只需使用root横向数据结构,而无需修改root

答案 2 :(得分:2)

这有点令人困惑,但我会试一试,也许我的解释方式对某人有意义:))

函数范围中的每个变量都以标准方式定义,基本上是..(变量类型)(变量名称)。不管那是:

int foo; // an int called foo

char *bar; // a char * called bar

struct xyz *blah; // a struct xyz * called blah
当您将它们作为参数传递给另一个函数时,

以及您对待foobarblah的方式是相同的。如果您希望被调用的函数只是查看或使用变量,您可以按原样(按值)传递它们,这会创建值的副本(int,或char的地址,或{的地址{1}})。因此,如果更改int的值或调用函数中struct xyz的地址,则仅更改原始变量的本地副本。

如果你想让被调用的函数实际改变原始变量的值(例如增量foo,malloc内存为bar,或者改变列表中的哪个元素指向),你需要告诉被调用函数WHERE使更改(通过引用传递它们),这会导致被调用的函数被声明为struct xyzf(int *foo)f(char **bar)

人们会陷入间接的层面,但是当你打电话给另一个函数时真正重要的是你对使用更改变量的意图在当地范围内。

答案 3 :(得分:1)

如果要更改传递给例程的事物,则会传递指针。你的困惑是因为这个东西也是一个指针。

因此,如果您想将指针传递给例程,并且您也希望(可能)修改指针本身,请使用双指针。

如果要将指针传递给例程,但您想要做的就是更改或查询指针指向的内容,使用单个指针。

这就是区别,你想改变指针还是想要访问指针所指向的内容。

答案 4 :(得分:0)

由于问题被标记为C和C ++,下面是对该视角的区别。这个答案不涉及C ++容器类或智能指针,它们通常在C ++代码中首选。下面是4种情况,其中两种可以修改调用者的结构和调用者的指针,另外两种只能修改给定结构的内容。

C ++修改指针

如果希望函数修改指针,并且在C ++中将指针值返回给调用者,则可以使用对指针的引用来执行此操作:

void clearNode(struct node *&n){
    n->field = 0; // modify caller's struct
    n = nullptr; // set caller's pointer to NULL (C++11)
}
...
struct node n; // value
struct node *np = &n; // pointer to node
clearNode(np); // OK, np becomes null, n.field becomes 0
clearNode(&np); // will not compile, &np is not right type
clearNode(&n); // will not compile, &n is not lvalue

C修改指针

在C中,相同的代码将是这样的(也适用于C ++,虽然上面的版本会更好更清洁):

void clearNode(struct node **n){
    (*n)->field = 0; // modify caller's struct
    *n = NULL; // set caller's pointer to NULL
}
...
struct node n; // value
struct node *np = &n; // pointer to node
clearNode(np); // will not compile, np is not right type
clearNode(&np); // OK, np becomes NULL, n.field becomes 0
clearNode(&n); // will not compile, &n is not of right type

C仅修改struct

但是如果我们用指针编写相同的代码,它的工作方式会略有不同:

void clearNode(struct node *n){
    n->field = 0; // modify caller's struct
    n = NULL; // change local parameter, which in this case has no effect anywhere
}
...
struct node n; // value
struct node *np = &n; // pointer to node
clearNode(np); // OK, except np is not modified, n.field becomes NULL
clearNode(&np); // will not compile, &np is not of right type
clearNode(&n); // OK, n.field becomes NULL

C ++仅修改struct

最后,C ++中的相同代码会更加清晰:

void clearNode(struct node &n){
    n.field = 0; // modify caller's struct
    // no pointer, nothing to set to NULL
}
...
struct node n; // value
struct node *np = &n; // pointer to node
clearNode(np); // will not compile, np is not of right type
clearNode(&np); // will not compile, &np is not of right type
clearNode(&n); // will not compile, &n is not of right type
// these work, n.field becomes 0:
clearnode(n);
clearnode(*np);

您的问题

因此,从上面可以看出,如果你需要修改调用者指针,将指针传递给指针(C和C ++)或者引用指针(C ++)。这是有代价的:你必须总是传递一个可修改的指针变量,而双重间接也有很小的开销。两者的语法如上所示。

如果您不需要修改调用者指针,但需要修改结构内容,则将指针(C和C ++)或引用(C ++)传递给struct。两者的语法如上所示。

第三种情况,当你不需要修改任何内容时,上面没有涉及,但有3个基本选择:按值传递(C和C ++,干净的语法但复制整个struct),通过指针传递给const(C和C ++,有点丑陋的语法,但只传递地址)或传递const引用(仅限C ++,干净的语法,只传递地址)。

总而言之,当您需要修改调用者的指针时,请使用双指针(或指针指针)。否则,传递指针(或引用),如果struct很小,则传递一个值。