【问题标题】:Operations with pointers in linked lists链表中的指针操作
【发布时间】:2019-01-24 13:41:25
【问题描述】:

一般来说,我知道一个指针存储了另一个位于计算机内存中的值的内存地址,例如:

int firstvalue= 5
int * p1;
p1 = &firstvalue;  // p1 = address of firstvalue

如果我们在链表中定义如下操作会发生什么? *current=*list是不是表示current指向的值等于list指向的值?如果我们定义ecur=current,这意味着什么?

int function(struct list_t **list){
    struct list_t *ecur=NULL;
    struct list_t *current=*list;
    ecur=current;
}

更新: *list=remove(*list, param1, param2) 有什么作用?为什么会这样?

remove 是一个返回修改后的list 列表的函数。

更新 2: 为什么我们需要定义一个指向指针的指针才能修改列表? *list是指向指针的指针吗?

【问题讨论】:

  • current 将指向 *list 指向的任何内容。 ecur 将指向 cur 指向的任何内容。
  • ecur=cur 表示您没有正确输入current,因此您会收到编译器错误。
  • @StoryTeller,抱歉打错了。已编辑。
  • ecur = current; 就像任何其他变量赋值一样。它赋予两个变量相同的值,因此它们都指向同一个地方。
  • @Someprogrammerdude 感谢您的回复,*list=*list_2 有什么作用?

标签: c pointers linked-list


【解决方案1】:

变量list 是一个指向结构list_t 的指针。如果我们(仅作为示例)假设该结构位于地址 2000 并且未命名指针位于地址 1000 它将如下所示:

然后你有添加两个新变量的初始化。两者都是指向结构 list_t 的指针。

struct list_t *ecur=NULL;
struct list_t *current=*list;

所以现在图片变成了:

请注意,current 的值与中间的“some-pointer”相同,因为它是分配给 current*list

那么你就有了任务:

ecur=current;

表示ecur得到与current相同的值并给出图片:

更新:*list=remove(*list, param1, param2) 有什么作用?

它改变了图片中间的“some-pointer”的值。例如,如果 remove 函数删除链表中的第一个元素,则需要这样做。

【讨论】:

  • 谢谢,但是我们怎么知道struct list_t是链表list
  • @Kevin 好吧,这只是一个猜测。顾名思义,它是但我们不能真正知道没有看到代码。无论如何,与代码中的指针无关。如果您有该结构的代码,我相信您会看到它有一个名为 next 的指针
  • 我的意思是,我已经定义了链表struct list_t {int val; struct list_t *next;}; struct list_t *list=NULL;的结构。我们怎么知道some-pointer 正好指向列表的第一个节点?
  • @Kevin 因为你还没有发布你所有的代码,我不得不再次猜测......但我很确定你调用这个函数:function(&list) 注意& 这个也意味着函数内部的list与函数外部的list不同。其实函数中的“some-pointer”其实就是函数外的list
  • 谢谢,我想我明白了。另外,不应该是cur: 1000,因为*lis 是指针的参数吗?是cur: 2000,因为我们将lis定义为指针的指针,所以有区别吗?
【解决方案2】:

为什么我们需要定义一个指向指针的指针来修改列表? *list 是指向指针的指针吗?

请记住,C 通过值传递所有函数参数 - 函数定义中的形式参数与函数调用中的实际参数在内存中是不同的对象。例如:

void swap( int a, int b )
{
  int tmp = a;
  a = b;
  b = tmp;
}

void foo( void )
{
  int x = 1;
  int y = 2;

  swap( x, y );
}

ax 是不同的内存对象,by 是不同的内存对象,因此交换abx 和@ 没有影响987654329@。为了交换xy 的值,您必须向它们传递指针

void swap( int *a, int *b )
{
  int tmp = *a;
  *a = *b;
  *b = tmp;
}

void foo( void )
{
  int x = 1;
  int y = 2;

  swap( &x, &y );
}

表达式 *ax 相同,因此写入*a 与写入x 相同。 *by 相同。

因此,为了让函数写入参数,您必须将 指针 传递给该参数:

void foo ( T *arg )
{
  *arg = new_value(); // writes a new value to the thing arg points to
}

void bar( void )
{
  T var;
  foo( &var );        // write a new value to var
}

这适用于任何非数组类型T。让我们将T替换为指针类型P *

void foo( P **arg )
{
  *arg = new_value(); // write a new *pointer* value to the thing arg points to
}

void bar( void )
{
  P *var;
  foo( &var );        // write a new pointer value to var
}

语义完全相同 - 只是类型发生了变化。

如果一个函数有可能修改一个list * 对象(比如将其指向一个新的列表头),那么您必须传递一个指向该list * 对象的指针:

void add_node( struct list_t **list, struct list_t *node )
{
  if ( !*list || (node->value < (*list)->value) ) // make node new head of list
    *list = node;
  else
    // add node somewhere else in the list
}

int main( void )
{
  struct list_t *list = NULL;
  ...
  struct list_t *node = newNode( value );
  add_node( &list, node );
  ...
}

【讨论】:

  • 感谢您的回复!
【解决方案3】:
TYPE *p = ptype /*variable of type: TYPE * */;

不是一个任务。这是一个初始化,对于auto-matic (=on-the-stack) p 可以重写为:

TYPE *p;
p = ptype;

(不是TYPE *p; *p=ptype; /*would be a type error*/

就你的例子而言:

struct list_t *current=*list;

设置current 将指向的位置(与*list 指向的位置相同(*list 也是一个指针,因为list 是一个双重间接指针)),而无需对当前将执行的任何操作初始化后指向 (*current)。

不过,所有这些都只是概念性的。您的函数没有任何外部可见的效果,因此优化编译器应该完全删除它的主体。

【讨论】:

  • 感谢您的回复,*list=*list_2 有什么作用?
  • @Kevin - list_2 是什么?它在您迄今为止发布的代码中的什么位置?
  • @JohnBode 澄清一下,list_2 是修改list 的函数的返回列表。我没有添加它,因为我不想失去问题的焦点。
  • @Kevin - 问题是您向我们询问的代码不在您的问题正文中(尤其是询问remove 的更新)。我们不是通灵者,我们能做的最好的事情就是根据命名约定进行猜测。列表管理有一些常见的操作,但如果没有看到您询问的实际代码,我们无法告诉您它为什么会这样做。
【解决方案4】:

我和this post 有类似的想法。我想重新安排一下你的功能,以便更容易理解发生了什么:

int function(struct list_t **list)
{
    struct list_t *current = *list;
    struct list_t *ecur    = current;
}

如果我们使用元素 foo 调用此函数,我们基本上会得到:

struct list_t foo      = { .data = "foo" };
struct list_t *bar     = &foo;
struct list_t **list   = &bar;
struct list_t *current = *list;
struct list_t *ecur    = current;

我们有五个声明和五个赋值。为了更好的可读性,我会把所有的东西都写下来,不要声明:

foo     = { .data = "foo" };
bar     = &foo;
list    = &bar;
current = *list;
ecur    = current;

现在,让我们来看看吧:

  1. foo 是一个结构。它包含上述数据字段。
  2. bar 是一个指向结构的指针。它包含foo的地址
  3. list 是指向结构指针的指针。它包含bar的地址
  4. current 是一个指向结构的指针。其中包含list的内容,也就是foo的地址
  5. ecur 是一个指向结构的指针。它与current 相同,包含地址bar

最后我们可以把整个例子简化成这样:

struct list_t foo   = { .data = "foo" };
struct list_t *ecur = &foo;

这是什么意思?

  • list:因为list 是一个指向指针的指针,所以您可以通过取消引用它来修改bar 以指向完全不同的东西(*list = ...
  • current/ecur:这也是 bar 最初指出的。通过取消引用,您可以更改数据字段本身((*ecur).data = "banana" 或更好的ecur-&gt;data

我希望我能澄清事情,不要让事情变得更糟;)

【讨论】:

    【解决方案5】:

    为什么我们需要定义一个指向指针的指针才能修改 列表?

    让我添加一个完整的程序,虽然很短,以更好地说明它。它定义了一个简单的链表并在保持它有序的同时构建它。是的,我知道简单地调用qsort() 会更容易,但我想演示如何添加一级间接(指向指针的指针)允许顺利插入元素,而无需测试特殊情况。

    // C pointer exercise: sort arguments
    #include <stdio.h>
    #include <strings.h>
    #include <stdlib.h>
    
    struct list
    {
        char *arg;
        struct list *next;
    };
    
    int main(int argc, char *argv[])
    {
        // pointer to base, running pointer and pointer to pointer
        struct list *base = NULL, *p, **pp;
    
        for (int i = 1; i < argc; ++i)
        {
            struct list *new_entry = malloc(sizeof(struct list));
            if (new_entry)
            {
                new_entry->arg = argv[i];
    
                // find where to insert new entry
                for (pp = &base; *pp; pp = &(*pp)->next)
                    if (strcasecmp(new_entry->arg, (*pp)->arg) < 0)
                        break;
    
                // insertion in a simply linked list
                new_entry->next = *pp;
                *pp = new_entry;
            }
        }
    
        // display and cleanup
        for (p = base; p;)
        {
            struct list * tmp = p->next;
            puts(p->arg);
            free(p);
            p = tmp;
        }
    
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-14
      • 1970-01-01
      • 1970-01-01
      • 2012-08-11
      • 1970-01-01
      • 1970-01-01
      • 2015-06-17
      相关资源
      最近更新 更多