【问题标题】:prototyping a function with a struct pointer as a parameter [closed]使用结构指针作为参数对函数进行原型设计[关闭]
【发布时间】:2012-11-07 22:07:43
【问题描述】:

我一直在纠结这个问题。我在 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文件,我得到更多错误。

【问题讨论】:

  • 对于声明,typedef 需要在范围内。 (所以#include "queue.h" 或任何标题。)
  • 如果我 #include "extern.h"(这是定义 struct 的地方)我在 extern.h 中得到一个错误:“无效的重新定义类型名称(队列)”
  • 前面的定义在哪里,又是什么?
  • 哦,因为 extern.h 和 proto.h 一样包含在每个文件中,所以 proto.h 重新包含了 extern.h,重新声明了这个结构...
  • 你在使用标题保护吗?

标签: c struct function-prototypes


【解决方案1】:

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

#ifndef FILE_H
#define FILE_H

/* Declarations etc. here. */

#endif

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

【讨论】:

  • 我会更进一步,并建议所有包含文件都应该有这样的保护......并且 +1 提到 __GUARD 在技术上违反了语言规范
【解决方案2】:

关于您的编译器错误:

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

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

所以这里有一些建议:

首先,标准做法是用 一个 '#ifndef' 以确保它只被包含一次:

#ifndef __HDR_H
#define __HDR_H

... code goes here ...

#endif

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

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

其次,你需要弄清楚struct标签之间的区别 和 typedef。

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

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'包含一个指针 到另一个“结构队列”。

所以在这个:

QUEUE foo;
struct queue bar;

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

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

第三,你应该谨慎使用 typedef。在你的情况下,我建议 完全摆脱它,只是总是使用“结构” 关键词。这使得更容易知道发生了什么,因为 局部结构变量在其声明中具有“结构”,因此 普通读者可以看到它是一个结构体,而不是一个重命名的 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 参数作为前向声明。这是合法的,但不是一个好主意。)

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

【讨论】:

  • 切勿使用以下划线开头的标识符,如__HDR_H。由于入侵了实现命名空间,这是未定义的行为。使用HDR_H 就可以了。
【解决方案3】:

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

typedef struct {
    ...
}queue;

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

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

【讨论】:

    猜你喜欢
    • 2011-10-24
    • 2013-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多