如何将一个结构体声明为外部的并定义其typedef

26

我正在尝试在C语言中实现树算法。我在一个独立的头文件(b_tree_ds.h)中声明了一个extern结构体。现在我计划在所有想要使用此结构体的源文件中导入该文件。因此,我必须在头文件中使用extern进行声明。

现在的问题是我也想定义它的typedef,但编译器报错说有多个存储类。我应该怎么做呢?

typedef extern struct node {
    struct node* left;
    struct node* right;
    int key;    // contains value
}NODE;
实际问题如下,但我仍然无法解决 ??? 最近我学习了如何使用头文件和多个源文件使代码具有可移植性和分层性。为了做到这一点,我尝试使用这个原则来创建我的树程序。以下是我的文件:
b_tree_ds.h - 这将包含一个树节点的数据结构的声明,可以调用不同功能实现树的各种函数(可能在不同的源文件中)
typedef struct node {
    struct node* left;
    struct node* right;
    int key;    // contains value
}NODE;

当我尝试像 typedef extern struct node 这样添加一个外部变量时,会出现多重存储类的错误,但如果我省略它,就会出现多重定义的错误。

这是我的其他源文件:

traverse.h - 包含 traverse 函数的声明。

void traverse_print (NODE* p);

在这里,我也遇到了未知标识符NODE的错误。

traverse.c - 包含该函数的定义。

#include <stdio.h>
#include "b_tree_ds.h"
#include "traverse.h"

void traverse_print(NODE* p)
{
    if(p->left != NULL)
    {
        traverse_print(p->left);
    }

    if (p->right != NULL)
    {
        traverse_print(p->right);
    }

    printf ("\n%d",p->key);
}

最终的 main.c 文件

#include <stdio.h>
#include "traverse.h"

void main()
{
    // input
    NODE p;

    printf("\nInput the tree");
    input_tree (&p);

    printf("\n\nThe tree is traversing ...\n")
    traverse_print(&p);
}

void input_tree (NODE *p)
{
    int in;
    int c;
    NODE *temp;

    printf("\n Enter the key value for p: ");
    scanf("%d", &in);
    p->key  =in;
    printf ("\n\nIn relation to node with value %d",in);
    printf ("Does it have left child (Y/N): ")
    if ((c = getchar()) == Y);
    {
        //assign new memory to it.
        temp = (NODE *)malloc(sizeof(NODE));
        input_tree(temp);
    }
    printf ("\n\nIn relation to node with value %d",p->key);

    printf ("\nDoes it have right child (Y/N): ")
    if ((c = getchar()) == Y);
    {
        //assign new memory to it.
        temp = (NODE *)malloc(sizeof(NODE));
        input_tree(temp);
    }
}

这是我第一次尝试这样的练习,请问我的程序结构是否良好,或者我应该尝试其他东西。

3个回答

34
你无法使一个结构体成为extern。只需在受到包含保护的头文件中定义它,并在需要时在任何地方包含该头文件即可。

针对SquareRootOfTwentyThree的编辑

我在此处采用以下方式使用这些术语:

结构体类型定义描述了组成结构体的成员。它包含了struct关键字,后面是一个可选的标识符(结构体标记)和一个用大括号括起来的成员列表。

结构体声明与结构体定义具有相同的形式,但声明没有用大括号括起来的成员列表。

所以,“definition”正是我所指的。

2
一般来说,定义意味着分配存储空间,这通常不应该在头文件中发生(无论它是否被保护)。在头文件中,您只能进行声明。因此,您的回答令人困惑。请引用C标准的参考资料来支持您的观点,而不是引用网上的某些解释。 - SquareRootOfTwentyThree
@SquareRootOfTwentyThree 我认为你在吹毛求疵。我坚持我的答案:在这个上下文中,“定义”是非常清晰的,你不会把它和其他任何东西混淆。 - cnicutar
1
不太清楚。你字面上说用户应该在头文件中“定义”结构体。只需将其更改为“声明”,就可以了。 - quant
你能不能像外部变量一样(稍作修改)extern一个结构体?创建一个包含 extern Struct1 S 的头文件 extern.h,再创建一个包含 typedef struct Struct1 的头文件 struct.h,最后在任何一个单独的 .c 文件中声明 Struct1 S。在需要使用此结构体的所有文件中包含头文件 extern.h 和 struct.h。现在可以在任何地方访问此结构体。 - AlphaGoku
我这样想:声明指定标识符。定义指定布局。实例化意味着分配存储空间。在定义中没有分配存储空间。接受此答案。 - average joe

33
在C语言中,结构体没有链接性,只有对象和函数拥有。因此你可以这样写:

在C语言中,结构体没有链接性,只有对象和函数拥有。因此你可以这样写:

// header file 'node.h'

typedef struct node_
{
    /* ... */
} node;

extern node root_node;

然后在某个地方提供一个实现:

// source file

#include <node.h>

node root_node;

2
“只有对象才能做到” :-? 那么您是否也包括函数在内呢? - cnicutar
1
@cnicutar:是的,我想是这样的 :-) 你可以获取一个函数的地址,不是吗? - Kerrek SB
2
C语言中的对象是什么? - Pithikos
2
@Pithikos:具有存储功能且可以作为值的事物。例如,一个 int - Kerrek SB
如果“object”是您用于表示链接的事物的词,那么“symbol”是否更正确?这是因为C语言没有对象的概念,这就是您正在寻找的吗? - Timothy John Laird
显示剩余2条评论

3
在您的头文件中像这样声明node.h。
#ifndef NODE_H
#define NODE_H

#ifdef  __cplusplus
extern "C" {
#endif

typedef struct node {
        struct node* left;
        struct node* right;
        int key;    // contains value
    }NODE;


#ifdef  __cplusplus
}
#endif

#endif  /* NODE_H */  

你可以在任何 C 语言程序中包含此头文件并像下面这样使用它:
NODE* newNode = NULL;

1
这会产生一个错误 错误:声明说明符中有多个存储类别 - simar
1
extern "C" {} 只在使用C++编译器时有效。 - KaiserKatze
1
这个特定的实现仍然可以在C编译器中工作。#ifdef __cplusplus保护确保只有C++编译器才能看到那部分内容。 - Rafael Dantas

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接