&引用;“无效,不可预期”;在C中

&引用;“无效,不可预期”;在C中,c,cc,minix,C,Cc,Minix,我有下面的代码在一个更大的C程序。我一直没有问题,直到刚才我试图编译它;它在Minix2.0.4中运行,并使用cc进行编译。将引发编译错误,如下所示: line 26: void not expected int main(int argc, char ** argv) { ... // (a) void initpool(void); ... initpool(); ... } 第26行只是main()中的函数声明: initpool() void initpool(v

我有下面的代码在一个更大的C程序。我一直没有问题,直到刚才我试图编译它;它在Minix2.0.4中运行,并使用
cc
进行编译。将引发编译错误,如下所示:

line 26: void not expected
int main(int argc, char ** argv) {
  ... // (a)
  void initpool(void);
  ...
  initpool();
  ...
}
第26行只是
main()
中的函数声明:

initpool()

void
initpool(void)
{
根据我的研究,一切都应该是正确的,
gcc
不会抛出编译错误。前面的所有行都以
结尾是他们应该做的,所以这不是问题所在。为什么
cc
在编译时遇到问题

编辑:根据要求,第26行之前的行如下(从
main()
开头开始,第25行为空):


很可能您的程序如下所示:

line 26: void not expected
int main(int argc, char ** argv) {
  ... // (a)
  void initpool(void);
  ...
  initpool();
  ...
}
用(a)表示的部分必须包含一些非声明语句。在旧的C编译器中,不允许在第一个非声明语句之后进行声明:

void foo() {
  int a;
  int b;
  foo();
  int c; // not allowed in old C
}
list = NULL;
因此,有两种可能的修复方法:

// the preferred fix for a single file
void initpool(void);
int main(int argc, char ** argv) {
  ... // (a)
  ...
  initpool();
  ...
}
void initpool(void) {}

// the technically correct fix
int main(int argc, char ** argv) {
  void initpool(void);
  ... // (a)
  ...
  initpool();
  ...
}
initpool
的转发声明实际上不属于
main
内部。为什么?因为你应该让编译器帮助你,你不应该重复你自己

就冗长而言,本地声明看起来非常愚蠢:

// Good                          // Correct but Verbose
void initpool(void);             void fun1(void) {
void fun1(void) {                  void initpool(void);
  initpool();                      initpool();
}                                }
void fun2(void) {                void fun2(void) {
  initpool();                      void initpool(void);
}                                  initpool();
                                 }
最后,假设
initpool()
在一个单独的文件中实现。然后你就可以随心所欲地做你想做的傻事了。例如:

// pool.c
void initpool(void) {
  ...
}
// main.c
int main() {
  void initpool(); // a common typo
  initpool(0);     // undefined behavior, your hard drive gets formatted
}
您应该在单独的头文件中包含池组件的公共API:

/// pool.h
void initpool(void);

/// pool.c
#include "pool.h"
void initpool(void) { ... }

/// main.c
#include "pool.h"
int main() {
  initpool();  // OK
  initpool(0); // the compiler will catch the mistake
}
例如,旧编译器会欣然接受以下内容:

void fun1() {
  void initpool(int);
}
void fun2() {
  void initpool(void);
}
最后,必须指出的是,在C中,以及在C(不是C++)中仅中,以下声明是兼容的,但这并不安全。行为由实现定义。例如,这种草率将使用stdcall生成无效的程序集

void bar();    // an unknown, fixed number of arguments
void bar(int,int,int,int);
void bar()
类似于
void bar(…)
,如果C允许的话。一些旧的C编译器确实允许省略,而不需要前面的参数


感谢Keith Thompson迫使我深入研究问题,并意识到我使用的一些编译器有多么糟糕:)

将问题中的代码片段放在一起,您有以下几点:

int
main(int argc, char *argv[])
{  
    int count, inserror;
    olnode *list, *ptr;
    list = NULL;

    void initpool(void);  /* line 26 */

    /* ... */
}
在1999年ISO C标准之前,C不允许在块中混合声明和语句。每个块(包括函数定义的外部块)必须包含零个或多个声明,后跟零个或多个语句

1999年的标准放宽了这一规则(遵循C++),但许多C编译器仍然默认执行C90规则。(C90有时被错误地称为“ANSI C”。)

你有一个声明:

void foo() {
  int a;
  int b;
  foo();
  int c; // not allowed in old C
}
list = NULL;
随后是一项声明:

void initpool(void);
将声明移到语句上方应该可以更正问题。使用编译器选项来使用C99或更高版本的标准也可以解决此问题,但这可能不可用,具体取决于您使用的编译器。gcc有
-std=c99
-std=gnu99
-std=c11
,和
-std=gnu11
;有关详细信息,请阅读gcc手册。我不知道什么是编译器“
cc
”;这是许多不同C编译器的通用名称

顺便说一句,将函数声明放在函数定义中有点不寻常。更常见的做法是将所有函数声明放在文件范围内,或者对于较大的项目,将声明放在头文件中,头文件是由定义函数的
.c
文件和包含对函数调用的任何
.c
文件创建的。显然你的导师坚持这种风格。这没有错,只要函数声明和定义出现在同一个源文件中,编译器就会诊断任何不一致

如果声明使用空括号,则存在潜在问题:

void initpool();
不幸的是,这与:

void initpool(int n) { /* ... */ }
但这与声明是否在函数体中无关,并且可以通过一致使用原型轻松避免


对于不带参数的函数,请使用
(void)
,而不是
()
。您已经在使用正确的原型;继续这样做。

第26行之前是什么?errro并不意味着第26行本身就是错误的。这意味着之前出现的任何内容都没有正确终止(例如,缺少
),使空白“不可预期”;这个错误很可能就在前面提到的那一行之前。有趣的是,如果你想的时间超过一秒钟,问题的根源就显而易见了,只要我们假设代码中没有其他错误,那么上下文就没有必要了。实际上,这相当简单:编译器处于ANSI模式,
initpool
的声明遵循非声明语句。任何处理C代码的人都应该能够很快地判断出问题所在:)我对C是全新的,这就是为什么我没有理解它。我对C几乎一无所知,当我习惯了Python和Java等高级语言时,转换到C语言并处理指针和其他事情是相当令人沮丧的。我上面的评论是针对那些不考虑问题而要求上下文的人的:)啊,就是这样。我知道变量声明必须在函数开始时进行,但我不知道函数声明也必须进行。我移动了
list=NULL到下面的函数声明,现在它已成功编译。非常感谢。呵呵。这实际上是针对一个类的,我的教授明确指出,函数必须在实际调用/使用它的函数中声明。你的解释完全有道理,如果我自己写代码,我肯定会这样做,但如果我们不按照教授的指示去做,exa