【问题标题】:Strange compiler warning C: warning: ‘struct’ declared inside parameter list奇怪的编译器警告 C:警告:在参数列表中声明的“结构”
【发布时间】:2013-05-30 08:44:32
【问题描述】:

我刚刚发现了一个 C 语言的怪癖,我觉得这很令人困惑。在 C 中,可以在声明结构之前使用指向结构的指针。这是一个非常有用的功能,因为当您只处理指向它的指针时,声明是无关紧要的。不过,我刚刚发现了一个极端案例,但(令人惊讶的是)这不是真的,我无法真正解释原因。在我看来,这似乎是语言设计中的一个错误。

获取此代码:

#include <stdio.h>

#include <stdlib.h>


typedef void (*a)(struct lol* etc);

void a2(struct lol* etc) {

}

int main(void) {
        return 0;
}

给予:

foo.c:6:26: warning: ‘struct lol’ declared inside parameter list [enabled by default]
foo.c:6:26: warning: its scope is only this definition or declaration, which is probably not what you want [enabled by default]
foo.c:8:16: warning: ‘struct lol’ declared inside parameter list [enabled by default]

要解决这个问题,我们可以这样做:

#include <stdio.h>

#include <stdlib.h>

struct lol* wut;

typedef void (*a)(struct lol* etc);

void a2(struct lol* etc) {

}

int main(void) {
        return 0;
}

这个无法解释的问题现在因为无法解释的原因消失了。为什么?

请注意,这个问题是关于语言 C 的行为(或者可能是 gcc 和 clang 的编译器行为),而不是我粘贴的具体示例。

编辑:

我不会接受“声明的顺序很重要”作为答案,除非您还解释了为什么 C 会警告第一次在函数参数列表中使用结构指针,但允许在任何其他上下文中使用它。为什么这可能是个问题?

【问题讨论】:

  • 你仍然应该使用struct lol;提前告诉它结构存在
  • 你应该先告诉编译器存在struct lol这样的类型,然后你可以在新函数的声明中使用struct lo或指向该struct的指针
  • 另外,编译器似乎向您解释了确切的问题:范围。阅读警告!

标签: c


【解决方案1】:

要理解编译器报错的原因,你需要知道关于 C "struct"s 的两件事:

  • 一旦您命名它们,它们就会被创建(作为已声明但尚未定义的类型),因此struct lol 的第一次出现会创建一个声明
  • 它们遵守与普通变量相同的“声明范围”规则

struct lol { 声明然后开始定义结构,它是 struct lol;struct lol * 或其他没有在“声明”步骤之后停止的左大括号的东西。)

已声明但尚未定义的结构类型是 C 称为“不完整类型”的实例。你可以使用指向不完整类型的指针,只要你不试图跟随指针:

struct lol *global_p;
void f(void) {
    use0(global_p);     /* this is OK */
    use1(*global_p);       /* this is an error */
    use2(global_p->field); /* and so is this */
}

换句话说,您必须完成类型才能“跟随指针”。

无论如何,请考虑使用普通 int 参数的函数声明:

int imin2(int a, int b); /* returns a or b, whichever is smaller */
int isum2(int a, int b); /* returns a + b */

这里名为ab 的变量是在括号内声明的,但这些声明需要避开,以便next 函数声明不会抱怨它们被重新定义- 声明。

struct 标签名也会发生同样的事情:

void gronk(struct sttag *p);

struct sttag 声明一个结构,然后声明被扫除,就像 ab 的声明一样。但这产生了一个大问题:标签消失了,现在你不能再命名结构类型了!如果你写:

struct sttag { int field1; char *field2; };

定义了一个新的不同的struct sttag,就像:

void somefunc(int x) { int y; ... }
int x, y;

在文件级别范围内定义了一个新的不同的xy,与somefunc 中的不同。

幸运的是,如果您在编写函数声明之前声明(甚至定义)结构,那么原型级声明“引用”外部范围声明:

struct sttag;
void gronk(struct sttag *p);

现在两个struct sttags 是“相同的”struct sttag,所以当你稍后完成struct sttag 时,你也在完成gronk 原型中的那个。


问题编辑:当然可以以不同方式定义结构、联合和枚举标签的操作,使它们从原型“冒泡”到它们的封闭范围。这将使问题消失。但它不是这样定义的。由于是 ANSI C89 委员会发明(或者实际上是从当时的 C++ 窃取)原型,因此您可以将其归咎于他们。 :-)

【讨论】:

  • 谢谢。我认为这是最有见地的答案。
  • 当他们发明这些(原型范围等)时,我就在附近。人们在开始使用原型时发现了原型内部结构声明的问题——经常从进行类型检查的编译器那里得到难以理解的错误消息,但只是说struct foo * is not compatible with struct foo * 而没有解释为什么。那是……有趣。 :-)
  • 你能提供一些标准的报价吗?
  • @GreenTree:哪一个,1989 年的原始 ANSI C 标准,还是更新的?
  • @torek,C99。 (怀疑措辞会有所不同,可能是段落编号)
【解决方案2】:

编译器警告您关于struct lol前向声明。 C 允许你这样做:

struct lol;     /* forward declaration, the size and members of
                   struct lol are unknown */

这在定义自引用结构时最常用,但在定义从未在标头中定义的私有结构时也很有用。由于后一种用例,允许声明接收或返回指向不完整结构的指针的函数:

void foo(struct lol *x);

但是,正如您所做的那样,仅在函数声明中使用未声明的结构将被解释为 struct lollocal 不完整声明,其范围受限于函数。这种解释是 C 标准强制要求的,但它没有用(无法构造 struct lol 以传递给函数),而且几乎可以肯定不是程序员想要的,因此编译器会发出警告。

【讨论】:

    【解决方案3】:

    这是因为,在第一个示例中,该结构先前未定义,因此编译器尝试将对该结构的首次引用视为定义。

    一般来说,C 是一种语言,声明的顺序很重要。您使用的所有内容都应事先以某种身份正确声明,以便编译器在其他上下文中引用它时可以对其进行推理。

    这不是语言设计中的错误或错误。相反,我相信这是为了简化第一个 C 编译器的实现而做出的选择。前向声明允许编译器一次性连续翻译源代码(只要知道一些信息,例如大小和偏移量)。如果不是这种情况,编译器将能够在遇到无法识别的标识符时在程序中来回切换,这要求其代码发射循环要复杂得多。

    【讨论】:

    • 从技术上讲,您仍然可以“一次性”完成所有操作,您只需在内存中保留更多数据。当然,在编译器中的简单与复杂方面也有类似的效果。 :-)
    • @torek 真的。我会看看我能不能更准确地说出来。
    【解决方案4】:

    注意拼写错误并仔细检查您的行号!我在编译之前连接了所有源文件,因此我正在使用的源代码中的行号毫无意义。我必须花额外的时间来打开连接的有效载荷并检查它。所以通常我不会,我只是假设我知道我从控制台输出消息中看到的是哪一行。

    示例: 海合会说:

    EXAMPLE.C11:27:1:错误:在内部声明了“struct THIS_STRUCT_IS_OK” 参数列表 [-Werror] ){

    #include <stdio.h> //:for: printf(...)
    
    struct THIS_STRUCT_IS_OKAY{
        int whatever;
    };
    
    
    int LookingAtThisFunction(
    
        struct THIS_STRUCT_IS_OKAY* arg
    
    ){ 
       //:    (Because you are not checking line numbers, you    )
       //:    (assume you are looking here. But you are not.     )
       //:    (Maybe you are concatenating all of your source    )  
       //:    (files together before compiling and line numbers  )
       //:    (don't correspond to the original source and you   )
       //:    (didn't examine your concatted source code payload?)
    
        return( arg -> whatever );
    
    }
    
    //:You are not looking here because this is later in the
    //:file, so the compiler would be complaining about the
    //:FIRST usage of the struct, not the second one, you assume.
    //:And you would be correct, if there wasn't a typo.
    void WhereYouAreNotLooking(
        struct THIS_STRUCT_IS_OK* arg
    ){ 
    
        LookingAtThisFunction( arg );
    
    }
    
    
    int main( void ){
    
    }
    

    总结:如果您知道错误消息的含义。你向上帝发誓 编译器坏了,因为你已经检查过了...... 1. 寻找错别字。 2. 确保您查看的是正确的行号。

    我知道这有点愚蠢。但它一直让我摸不着头脑 半小时。所以希望它可以帮助那些已经看过 显而易见的解决方案。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-08-19
      • 1970-01-01
      • 2014-04-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多