C语言 奇怪的编译器警告 C: 警告: 'struct' 在参数列表中声明
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16831605/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Strange compiler warning C: warning: ‘struct’ declared inside parameter list
提问by Hannes Landeholm
I just found a quirk in C that I find really confusing. In C it's possible to use a pointer to a struct before it has been declared. This is a very useful feature that makes sense because the declaration is irrelevant when you're just dealing with a pointer to it. I just found one corner case where this is (surprisingly) not true, though, and I can't really explain why. To me it looks like a mistake in the language design.
我刚刚在 C 中发现了一个让我感到非常困惑的怪癖。在 C 中,可以在声明之前使用指向结构的指针。这是一个非常有用的特性,因为当您只是处理指向它的指针时,声明是无关紧要的。我刚刚发现了一个角落案例,其中(令人惊讶地)不是真的,但我无法真正解释原因。对我来说,这看起来像是语言设计中的一个错误。
Take this code:
拿这个代码:
#include <stdio.h>
#include <stdlib.h>
typedef void (*a)(struct lol* etc);
void a2(struct lol* etc) {
}
int main(void) {
return 0;
}
Gives:
给出:
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]
To remove this problem we can simply do this:
要解决这个问题,我们可以简单地这样做:
#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;
}
The unexplainable problem is now gone for an unexplainable reason. Why?
无法解释的问题现在由于无法解释的原因消失了。为什么?
Note that this question is about the behavior of language C (or possible the compiler behavior of gcc and clang) and not the specific example I pasted.
请注意,这个问题是关于 C 语言的行为(或者可能是 gcc 和 clang 的编译器行为),而不是我粘贴的具体示例。
EDIT:
编辑:
I won't accept "the order of declaration is important" as an answer unless you also explain why C would warn about using a struct pointer for the first time in a function argument list but allow it in any other context. Why would that possibly be a problem?
我不会接受“声明的顺序很重要”作为答案,除非您还解释了为什么 C 会警告第一次在函数参数列表中使用结构指针但允许在任何其他上下文中使用。为什么这可能是个问题?
回答by torek
To understand why the compiler complains, you need to know two things about C "struct"s:
要了解编译器为什么会抱怨,您需要了解有关 C 的“结构”的两件事:
- they are created (as a declared, but not yet defined, type) as soon as you name them, so the very first occurrence of
struct lolcreates a declaration - they obey the same "declaration scope" rules as ordinary variables
- 一旦你命名它们,它们就会被创建(作为一个已声明但尚未定义的类型),所以第一次出现时
struct lol会创建一个声明 - 它们遵守与普通变量相同的“声明范围”规则
(struct lol {declares and then begins defining the structure, it's struct lol;or struct lol *or something else that does not have the open-brace that stops after the "declare" step.)
(struct lol {声明然后开始定义结构,它struct lol;或struct lol *或其他没有在“声明”步骤后停止的开放括号的东西。)
A struct type that is declared but not yet defined is an instance of what C calls an "incomplete type". You are allowed to use pointers to incomplete types, as long as you do not attempt to follow the pointer:
已声明但尚未定义的结构类型是 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 */
}
You have to complete the type in order to "follow the pointer", in other words.
换句话说,您必须完成类型才能“跟随指针”。
In any case, though, consider function declarations with ordinary intparameters:
但是,无论如何,请考虑使用普通int参数的函数声明:
int imin2(int a, int b); /* returns a or b, whichever is smaller */
int isum2(int a, int b); /* returns a + b */
Variables named aand bhere are declared inside the parentheses, but those declarations need to get out of the way so that the the nextfunction declaration does not complain about them being re-declared.
变量 nameda和bhere 在括号内声明,但这些声明需要避开,以便下一个函数声明不会抱怨它们被重新声明。
The same thing happens with structtag-names:
struct标签名也会发生同样的事情:
void gronk(struct sttag *p);
The struct sttagdeclares a structure, and then the declaration is swept away, just like the ones for aand b. But that creates a big problem: the tag is gone and now you can't name the structure type ever again! If you write:
Thestruct sttag声明了一个结构,然后声明就被扫除了,就像 fora和 一样b。但这产生了一个大问题:标签消失了,现在你不能再命名结构类型了!如果你写:
struct sttag { int field1; char *field2; };
that defines a new and different struct sttag, just like:
定义了一个新的和不同的struct sttag,就像:
void somefunc(int x) { int y; ... }
int x, y;
defines a new and different xand yat the file-level scope, different from the ones in somefunc.
定义了一个新的、不同的x并且y在文件级范围内,与somefunc.
Fortunately, if you declare (or even define) the struct beforeyou write the function declaration, the prototype-level declaration "refers back" to the outer-scope declaration:
幸运的是,如果在编写函数声明之前声明(或什至定义)结构,原型级声明“返回”到外部作用域声明:
struct sttag;
void gronk(struct sttag *p);
Now both struct sttags are "the same" struct sttag, so when you complete struct sttaglater, you're completing the one inside the prototype for gronktoo.
现在两个struct sttags 都是“相同的” struct sttag,所以当你struct sttag稍后完成时,你也在完成原型中的一个gronk。
Re the question edit: it would certainly have been possible to define the action of struct, union, and enum tags differently, making them "bubble out" of prototypes to their enclosing scopes. That would make the issue go away. But it wasn't defined that way. Since it was the ANSI C89 committee that invented (or stole, really, from then-C++) prototypes, you can blame it on them. :-)
关于问题编辑:当然可以以不同的方式定义 struct、union 和 enum 标签的操作,使它们从原型“冒泡”到它们的封闭范围。这样问题就迎刃而解了。但它不是那样定义的。由于是 ANSI C89 委员会发明(或窃取,实际上,从当时的 C++)原型,您可以将其归咎于他们。:-)
回答by Theodoros Chatzigiannakis
This is because, in the first example, the struct is previously undefined and so the compiler tries to treat this first reference to that struct as a definition.
这是因为,在第一个示例中,该结构体之前未定义,因此编译器尝试将对该结构体的第一个引用视为定义。
In general, C is a language where the order of your declarations matters. Everything you use should be properly declared in advance in some capacity, so that the compiler can reason about it when it's referenced in other context.
一般来说,C 是一种声明顺序很重要的语言。您使用的所有内容都应该以某种身份事先正确声明,以便编译器在其他上下文中引用时可以对其进行推理。
This is not a bug or a mistake in the design of the language. Rather, it's a choice that I believe was made to simplify the implementations of the first C compilers. Forward declarations allow a compiler to translate the source code serially in one pass (as long as some information such as sizes and offsets is known). If this weren't the case, the compiler would have be able to go back and forth in the program whenever it meets an unrecognized identifier, requiring its code emission loop to be much more complex.
这不是语言设计中的错误或错误。相反,我认为这是为了简化第一个 C 编译器的实现而做出的选择。前向声明允许编译器在一次传递中串行翻译源代码(只要一些信息,如大小和偏移量是已知的)。如果不是这种情况,编译器将能够在遇到无法识别的标识符时在程序中来回切换,这需要其代码发射循环复杂得多。
回答by user4815162342
The compiler is warning you about a forward declarationof struct lol. C allows you to do this:
编译器警告你一个向前声明的struct lol。C 允许你这样做:
struct lol; /* forward declaration, the size and members of
struct lol are unknown */
This is most used when defining self-referencing structs, but it is also useful when defining private structs that are never defined in the header. Because of this latter use case, it is allowed to declare functions that receive or return pointers to incomplete structs:
这在定义自引用结构时最常用,但在定义从未在头中定义的私有结构时也很有用。由于后一种用例,允许声明接收或返回指向不完整结构的指针的函数:
void foo(struct lol *x);
However, just using an undeclared struct in a function declaration, as you did, will be interpreted as a localincomplete declaration of struct lolwhose scope is constrainted to the function. This interpretation is mandated by the C standard, but it is not useful (there is no way to construct the struct lolto pass to the function) and is almost certainly not what the programmer intended, so the compiler warns.
但是,像您一样在函数声明中使用未声明的结构体,将被解释为局部不完整声明,struct lol其作用域受限于函数。这种解释是 C 标准强制要求的,但它没有用(无法构造struct lol传递给函数的 ),并且几乎可以肯定不是程序员的意图,因此编译器警告。
回答by J.M.I. MADISON
Lookout for typos and DOUBLE CHECK your line numbers!I concat all my source files before compiling, so the line numbers from the source I am working in are meaningless. I have to take extra time to open up the concatted payload and examine it. So usually I don't and I just assume I know what line I am looking at from the console output message.
注意错别字并仔细检查您的行号!我在编译之前连接了我所有的源文件,所以我正在使用的源代码中的行号毫无意义。我必须花额外的时间打开连接的有效载荷并检查它。所以通常我不这样做,我只是假设我知道我从控制台输出消息中看到的是哪一行。
Example: GCC Says:
示例:GCC 说:
EXAMPLE.C11:27:1: error: 'struct THIS_STRUCT_IS_OK' declared inside parameter list [-Werror] ){
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 ){
}
In summary: If you know what the error message means. And you swear to god the compiler is broken because you already checked that... 1. Look for typos. 2. Make sure you are really looking at the correct line number.
总结:如果您知道错误消息的含义。你向上帝发誓编译器坏了,因为你已经检查过了... 1. 查找拼写错误。2. 确保您确实查看了正确的行号。
I get that this is kinda stupid. But it had been scratching my head for half an hour. So hopefully it helps someone who's already looked at the obvious solutions.
我知道这有点愚蠢。但它已经挠了我的头半个小时。所以希望它可以帮助那些已经看过明显解决方案的人。

