我一直在圈子里遇到这个问题。我有一个在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文件中,如果我尝试键入它或重新定义它或者甚至包含.h文件,那么它们就不是这个结构的队列的typedef声明,我得到更多错误。
答案 0 :(得分:3)
您可以防止使用 include guard 重复包含同一文件:
#ifndef FILE_H
#define FILE_H
/* Declarations etc. here. */
#endif
通过这种方式,您可以自动防止重新定义,例如您在typedef中观察到的重新定义。尽管您可能经常看到以下划线开头的宏作为包含保护,但这是不好的做法,因为这些名称位于实现的名称空间中,因此保留。
答案 1 :(得分: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 ')'
同上。此外,编译器期待之后的另一个单词 'struct'如果没有那个就会变得很困惑。
int QGetSize(struct queue *);
WARNING: struct declared inside parameter list
这意味着尚未定义类型'struct queue'。哪个呢 没有。 'typedef struct {...} queue'不同于 'struct queue {...}'。
所以这里有一些建议:
首先,标准做法是用头文件包装主体 '#ifndef'确保它只被包含一次:
#ifndef __HDR_H
#define __HDR_H
... code goes here ...
#endif
(__ HDR_H必须是唯一的,通常是文件名的变体。)
然后,您始终可以#include定义类型的标头 任何使用它们的文件。 #ifndef(通常)允许你包括 惩罚。
其次,你需要弄清楚struct标签之间的区别 和typedef。
struct标签是特定结构定义的名称:
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变量在其声明中有'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 { ... };
这很少是必要的,但你偶尔会发现自己 一些真正扭曲的循环依赖。在这种情况下,这可以得到 你出了果酱。 (这也是上面最后一个编译器警告的原因 警告,而不是错误;编译器解释了未知 struct参数作为前向声明。这是合法的,但是一个坏主意。)
无论如何,我希望这会有所帮助并祝你好运。
答案 2 :(得分:0)
在C中,有两种不同的类型名称空间:struct / union / enum标记名称的名称空间和typedef名称的名称空间。
typedef struct {
...
}queue;
上述声明声明了一个匿名结构并为其创建了一个typedef。它只在typedef命名空间中有一个名称,但在标记名称空间中没有名称。这意味着它无法向前宣布。如果要进行前向声明,则必须在标记名称空间中为其命名。