使用结构指针作为参数对函数进行原型设计

时间:2012-11-07 22:07:44

标签: c struct function-prototypes

我一直在圈子里遇到这个问题。我有一个在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声明,我得到更多错误。

3 个答案:

答案 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命名空间中有一个名称,但在标记名称空间中没有名称。这意味着它无法向前宣布。如果要进行前向声明,则必须在标记名称空间中为其命名。

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