【问题标题】:Using functions in C, declaring a variable and using it in multiple functions在 C 中使用函数,声明一个变量并在多个函数中使用它
【发布时间】:2013-10-05 20:26:23
【问题描述】:

我想创建一个链表,从其他地方的主目录调用。这个链表是由节点组成的。

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

nodeTest.h

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

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

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

nodeTest.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 行收到错误,头部无法识别。

【问题讨论】:

  • 1.您不需要在 C 程序中强制转换 malloc 的返回值。 2.请显示实际代码;您的第一个 malloc 调用甚至不在函数中。
  • 为什么不需要退货?这基本上是实际代码。需要改变什么?你需要看什么?
  • 编译的代码示例;还有错误是什么样的?
  • @PaulGriffiths 我不这么认为,我想知道更多关于这段代码如何与主文件一起工作以及专门调用其他函数的细节。

标签: c struct


【解决方案1】:

没错。您会收到该错误,因为变量 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 作为参数并可能需要更改它的函数都将接受一个双指针:一个指向头指针的指针。这很好用,但会使事情复杂化,并且可能会让一些新手程序员措手不及。它也有点容易出错,但没有上一个选项那么多。

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

【讨论】:

  • @ripDaddy69:嗯,这是你的功能;我刚打电话。我不确定你想让它做什么,但我猜从它的名字可以看出它应该在链表中添加一个条目。它如何做到这一点取决于对您施加的要求。如果您愿意,您可以就如何使addLL 工作提出单独的问题,但我建议您先尝试使用纸和铅笔,并将节点表示为框和箭头指针。然后,如果您无法解决,请再次询问。
  • @ripDaddy69 如果您有多个链表实例,则需要多个变量来表示它们,因此需要多个函数(或某种识别函数应使用哪个列表的方法)。如果您有多个列表,则几乎需要使用选项 2(无论如何,这是 明智的 方法,即使稍微复杂一点)。
  • @ripDaddy69 你没有 - 我只是为了演示这两种方法的不同(全局与指针)。至于语法/怪癖……那是您必须努力解决的问题。如果需要,您可以搜索并查看许多链表实现,以了解该语言及其工作原理。来自 Google 的第一个结果将我指向 cprogramming.com/snippets/source-code/c-linked-list,虽然我没有费心仔细阅读代码来验证它是否很棒,但我认为这可能是一个很好的开始,让您了解事情是如何运作的。
  • @ripDaddy69 main 函数是您的程序开始执行的地方。不过,您可以从任何地方执行此操作,但这取决于您采用哪种方法来实现必须执行的操作。如果您向我们展示实际代码,也许我们可以提供帮助,但在我看来,您确实应该花一些时间阅读一本关于 C 编程的书。如果您有编程经验(听起来像),那么您应该能够相对较快地掌握基础知识。
  • @NikBougalis 是的,我现在正在学习一些在线教程,但我只是不熟悉如何在没有 main 的情况下在链接列表中声明头部。
【解决方案2】:

问题0:链表和C的理解

上面有大量的材料。看看这些slides from a lecture on data structures

问题 1:'head undeclared'

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

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

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

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

要么先声明(与head 相同),要么使用head-&gt;data

问题 3:列表设计和 API

使用全局状态通常被认为是不好的做法,因此不要使用全局列表。 我建议您为列表定义另一个结构并在其他函数(如list_add()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 
}

【讨论】:

  • 我对向此列表中添加元素不感兴趣,我更感兴趣的是在列表中搜索元素。 *e 是做什么的?我不明白。头文件是什么意思?
  • 我现在并不真正关心这些,我只是在试图尽快完成这项工作,因为我的工作远远落后。我今天必须这样做。我已经花了 8 个小时在上面,但我取得的进展为零。我需要进步,否则我就完蛋了。
  • 如果 (first.data == 0),我将首先为该行未声明
  • int main(void) { struct nodeTest* first = malloc(sizeof(struct nodeTest));第一->数据= 0;返回0; } void addLL(int data){ if (first.data = 0)
  • 我正在处理几个版本的代码,但我不小心混合了其中的两个。这些是唯一的区别。
【解决方案3】:

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

typedef struct First {

<element 1>
struct first *next

} First;

那么你可以这样做

First *first = NULL
first = (First *) malloc(sizeof(First));

【讨论】:

  • 看起来不会编译
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-30
  • 1970-01-01
  • 1970-01-01
  • 2023-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多