-4

我想制作一个从我的主要地方调用的链接列表。该链表由节点组成。

例如,这不是我的代码,只是它的简化版本。

节点测试.h

#include <stdio.h>
#include <stdlib.h>

struct nodeTest
{
    int data;
    struct nodeTest* next;
};

然后我有另一个文件试图使用该结构:

节点测试.c

 #include <stdio.h>
 #include <stdlib.h>
 #include "nodeTest.h"

 int main(void) {
     struct nodeTest* first = malloc(sizeof(struct nodeTest));
     first->data = 0;
     return 0;
 }

void addLL(int data){
    if (first.data = 0)
    {
        printf("No head\n");
    }
}

我想首先成为链表中的第一个元素,那么如何为它定义一个首先使用的 addElement 呢?现在,当我第一次打电话时,我遇到了一个无法识别的错误。

这是我在主链表文件中得到错误的地方

void addLL(int data){
    if (head.data = 0)

我在 if 行收到错误,头部无法识别。

4

3 回答 3

2

正确的。您会收到该错误,因为该变量head仅在内部可见main。有两种方法可以解决这个问题:

  1. 您可以head在任何函数之外声明为全局变量;或者
  2. 您可以将head其作为指针传递给任何需要它的函数

让我们看看这些选项是如何工作的:

使用全局变量

#include <stdio.h>
#include <stdlib.h>
#include "nodeTest.h"

/* Declare a global variable. The initialization to NULL is redundant
 * since global variables are automatically initialized to zero. But
 * let's be thorough.
 */

struct nodeTest* head = NULL;

int main(void) {
   head = malloc(sizeof(struct nodeTest));
   /* check if head is NULL here. Just in case. */
   head->data = 0;

   addLL(1);

   return 0;
}

/* other functions here */
void addLL(int data) {
   /* since we allocate head in main it should never be NULL and
    * if it is, something is wrong. So assert.
    */
   assert(head != NULL);

   /* Notice: in your original code you had: if (first.data = 0)
    * which is wrong. First of all, there's no variable named first
    * declared anywhere. If you meant head, that should have been
    * head->first since head is a pointer. And lastly, first.data = 0
    * would set first.data to 0, instead of comparing it for equivalency, 
    * and would then cause the if to never execute because the compiler
    * would first set first.data to zero, then check first.data, and never
    * enter the loop since it was zero.
    */
   if (head->data == 0)
   {
      printf("No head");
      return;
   }

   /* other code here */
}

这个版本很简单,只需要很少的改动。一个好处是需要修改 head 的函数可以轻松完成。

但是,在非平凡的程序中,它确实需要大量的全局变量,这被认为是不好的编程习惯,应该尽可能避免。

head作为指针传递

#include <stdio.h>
#include <stdlib.h>
#include "nodeTest.h"

int main(void) {
   struct nodeTest* head = malloc(sizeof(struct nodeTest));
   /* check if head is NULL here. Just in case. */
   head->data = 0;

   /* we call addLL, passing the head that we just allocated
    * to it.
    */
   addLL(1, head);
   return 0;
}

void addLL(int data, struct nodeTest *head) {
   if (head == NULL) || (head->data == 0))
   {
      printf("No head");
      return;
   }

   /* other code here */
}

这种方法要求我们传入head每个可能需要它的函数。这会使函数接口稍微复杂化,但让我们更加灵活,因为我们不需要为程序中的许多列表提供许多全局变量。

但是还有另一个问题。如果这些函数中的任何一个需要修改head指针(例如,假设您要删除列表中的第一个元素),那么您就有问题了。指针(与指向的东西不同)是函数的本地指针,因此对其所做的任何更改都将在函数之外不可见。

有两种方法可以解决此问题:

  1. 每个接受head作为参数并可能需要更改它的函数都必须返回一个指向新头的指针。这可行,但实施起来很麻烦,而且很容易出错。
  2. 每个接受head作为参数并可能需要更改它的函数都将接受一个双指针:一个指向头指针的指针。这很好用,但会使事情复杂化,并且可能会让一些新手程序员措手不及。它也有点容易出错,但不像以前的选项那么多。

我希望这能回答你的问题,解释为什么你不能做你所做的事情以及存在的解决方法。

于 2013-10-05T21:13:24.693 回答
1

问题0:链表和C的理解

上面有很多材料。例如,查看数据结构讲座中的这些幻灯片。

问题 1:“头部未申报”

在使用变量之前,您需要先引入它。问题是由于head不在函数范围内(根据 C 规则)。因此,您需要将指向它的指针作为参数传递:

void addLL(struct nodeTest *head, int data){
  // now you can access head->data, head->next
  // .. 
}

如果您使用0表示没有数据可用,那么您不能存储任何零。另一个你可以保持列表元素的计数并检查计数是否为零以确定列表是否为空。

问题 2:'first->data' 未声明

要么先声明(与 相同head),要么使用head->data.

问题3:列表设计和API

使用全局状态通常被认为是不好的做法,所以不要使用全局列表。我建议您为列表定义另一个结构并在其他函数周围传递一个点list_add()or list_remove(),因此您还可以保存指向最后一个元素的指针以进行 O(1) 操作,例如:

struct list {
  struct node *first;
  struct node *last;
  unsigned long length;
};

那么,你可以实现一个函数来检查列表是否为空:

inline bool list_is_empty(struct list* l) {
  assert(l != NULL);
  return l->length;  
}

并在你的使用多个列表main

 struct list *list1 = list_create(); // sets length to 0
 struct list *list2 = list_create(); // return a new list
 //...
 list_add(&list1, data); // you pass a pointer to the list to which you want to add
 //...
 struct node *e = list_search(&list1, data);
 if (e == NULL)
   printf("not found\n");
 //...
 list_remove(&list1, data);
 //
 list_destroy(&list1); 

向列表中添加元素可以这样实现:

int list_add(struct list* l, int data) {

    if (l == NULL)
        return LIST_FAILURE; // or exit with an error message

    l->last->next = list_elem_create(data); // dynamically creates a new node
    l->last = l->last->next;
    l->length++;
    return LIST_SuCCESS; // symbolc constant defined elsewhere 
}
于 2013-10-05T20:36:22.380 回答
0

在调用 main 函数之前不能分配内存。您可以在 main 之外声明一个全局变量并将其地址用作第一个。main 中的 alloc 应该可以工作,这是最好的方法。但是您必须对结构进行类型转换,如下所示才能使用它,否则您的编译将失败

typedef struct First {

<element 1>
struct first *next

} First;

那么你可以这样做

First *first = NULL
first = (First *) malloc(sizeof(First));
于 2013-10-05T20:29:26.140 回答