0

我一直在纠结这个问题。我在 extern.h 中声明了一个结构:

typedef struct {
    char data[Q_SIZE];
    int head;
    int tail;
    int size;
}queue;

在 main.c 我声明了这个结构的两个实例:

queue rxq, txq;

在其他 .c 文件中,如果使用这些结构,它们将被声明为全局外部,即:

extern queue rxq, txq;

在 queue.c 中有几个函数接受指向这些结构之一的指针作为参数:

int QGetSize(queue * q)
{
    return q->size;
}

我的编译器要求我对这些函数进行原型制作,但不喜欢我制作它们原型的方式:

int QGetSize(queue *);
ERROR: parse error at near '*'

int QGetSize(queue);
ERROR: invalid functions argument declaration

int QGetSize(struct *);  // this is the one that to me, should work. the other errors make sense.
ERROR: parse error at near '*'

int QGetSize(struct);
ERROR: parse error at near ')'

int QGetSize(struct queue *);
WARNING: struct declared inside parameter list

可能需要注意的是,在prototype.h文件中,它们不是队列的typedef作为这个结构,如果我尝试对它进行typedef或重新定义它甚至包含最初声明它的.h文件,我会得到更多错误。

4

3 回答 3

3

您可以使用include guard防止重复包含同一文件:

#ifndef FILE_H
#define FILE_H

/* Declarations etc. here. */

#endif

这样你就可以自动防止像你观察到的 typedef 那样的重定义。尽管您可能经常看到以下划线开头的宏用作包含保护,但这是一种不好的做法,因为这些名称位于实现的命名空间中,因此是保留的。

于 2012-11-07T22:24:39.170 回答
1

关于您的编译器错误:

int QGetSize(queue *);
ERROR: parse error at near '*'

这可能是因为您没有包含 extern.h,或者如果包含,则未定义“queue”的 typedef。如果 C 找到一个应该是类型但还没有(还)被定义为类型的词,它会非常困惑。

int QGetSize(queue);
ERROR: invalid functions argument declaration

和上面一样。定义必须出现在使用之前。

int QGetSize(struct *);
ERROR: parse error at near '*'

这是无效的。'struct' 不是类型;'struct queue' 是类型。也就是说,您需要在 'struct' 之后指定类型标记才能使其有意义。

int QGetSize(struct);
ERROR: parse error at near ')'

同上。此外,编译器会在“结构”之后期待另一个词,如果没有的话会非常困惑。

int QGetSize(struct queue *);
WARNING: struct declared inside parameter list

这意味着尚未定义类型“结构队列”。它没有。'typedef struct { ... } queue' 与'struct queue { ... }' 不同。

所以这里有一些建议:

首先,标准做法是用“#ifndef”包装头文件的主体,以确保它只被包含一次:

#ifndef __HDR_H
#define __HDR_H

... code goes here ...

#endif

(__HDR_H 必须是唯一的,并且通常是文件名的变体。)

然后,您始终可以将定义类型的标头#include 到使用它们的任何文件中。#ifndef(通常)允许您不受惩罚地包含在内。

其次,你需要弄清楚结构标签和类型定义之间的区别。

结构标记是特定结构定义的名称:

struct queue { ... };       // Defines struct queue

struct queue foo;           // foo is a queue structure.

typedef 是现有类型的别名:

typedef int HANDLE;         // HANDLE is just another way of saying 'int'

typedef struct queue QUEUE; // QUEUE is shorthand for 'struct queue'

两者是非常不同的东西。第一个定义了特定类型的结构,而第二个为现有类型创建了一个新名称。事实上,您可以将它们组合起来:

typedef struct queue { ... } QUEUE;

你经常想要,特别是如果'struct queue'包含指向另一个'struct queue'的指针。

所以在这个:

QUEUE foo;
struct queue bar;

'foo' 和 'bar' 具有完全相同的类型。

(此外,与此半相关:C 中的常见做法是使 typedef 名称全部大写。)

第三,你应该谨慎使用 typedef。在你的情况下,我建议完全摆脱它,只使用'struct'关键字。这使得知道发生了什么变得容易得多,因为局部结构变量在其声明中具有“结构”,因此不经意的读者可以看到它是一个结构,而不是重命名的 int。

更重要的是,它可以让编译器为您提供更有意义的错误消息。如果结构未定义并且编译器看到如下内容:

int foo(struct bar x);

它知道 bar 是一个结构,所以整个事情是一个参数声明,可以告诉你“struct bar”是未定义的。但是,如果它看到这一点:

int foo(BAR x);

它不知道 BAR 应该是什么,所以错误消息往往是一个很大的 WTF?!?!?!?!?反而。

最后,如果你使用 'struct' 形式,你可以预先声明结构:

struct bar;
int foo (struct bar x);
struct bar { ... };

这很少有必要,但您偶尔会发现自己有一些非常扭曲的循环依赖。在这种情况下,这可以让你摆脱困境。(这也是为什么上面的最后一个编译器警告是警告而不是错误的原因;编译器将未知的结构参数解释为前向声明。这是合法的,但不是一个好主意。)

无论如何,我希望这会有所帮助并祝你好运。

于 2012-11-07T23:02:40.543 回答
0

在 C 中,有两种不同的类型命名空间:结构/联合/枚举标记名称的命名空间和 typedef 名称的命名空间。

typedef struct {
    ...
}queue;

上面的声明声明了一个匿名结构并为其创建了一个 typedef。它在 typedef 命名空间中只有一个名称,但在 tag 命名空间中没有名称。这意味着它不能被前向声明。如果要进行前向声明,则必须在标签命名空间中为其命名。

请进一步参考此https://stackoverflow.com/a/612350/1450257

于 2012-11-07T22:50:30.130 回答