3

I have the below code in a larger C program. I've had no issues until just now when I tried to compile it; it's being run in Minix 2.0.4 and compiled using cc. A compilation error is thrown as follows:

line 26: void not expected

Line 26 is simply a function declaration inside main():

void initpool(void);

initpool() itself is defined later in the program with this header:

void
initpool(void)
{

From what I've researched everything should be correct, and gcc throws no compilation errors. All previous lines do end with ;s as they should, so that's not the issue. Why is cc having a problem compiling it?

EDIT: As requested, the lines leading up to line 26 are as follows (starting at the beginning of main(), line 25 is blank):

19: int
20: main(int argc, char *argv[])
21: {
22:     int count, inserror;
23:     olnode *list, *ptr;
24:     list = NULL;
vaindil
  • 7,536
  • 21
  • 68
  • 127
  • 7
    what comes BEFORE line 26? the errro doesn't mean line 26 is itself incorrect. it means whatever comes before there hasn't been terminated correctly (e.g. missing `;`), making the void "not expected". – Marc B Feb 03 '15 at 19:19
  • 1
    We need a bit more context; the error is likely immediately before the line mentioned. – Carl Norum Feb 03 '15 at 19:19
  • The interesting thing here is that if you think about it for longer than a second, it's exactly apparent what the source of the problem is, and the context is unnecessary as long as we assume that there are no other errors in the code. It's rather simple, really: the compiler is in ANSI mode and `initpool`'s declaration follows a non-declaration statement. Anyone who deals with C code should be able to pretty much immediately tell what's wrong :) – Kuba hasn't forgotten Monica Feb 03 '15 at 19:27
  • I'm brand new to C which is why I didn't catch it. I know almost nothing about C, and it's been rather frustrating switching to the language and dealing with pointers and things when I'm used to higher-level languages like Python and Java. – vaindil Feb 03 '15 at 19:30
  • 1
    My comment above was meant for those who demanded context without thinking about the question :) – Kuba hasn't forgotten Monica Feb 03 '15 at 19:34
  • Context is still helpful in providing the most concrete answer possible, in terms of fixed code. Anyone literate enough in C to understand what you're saying, wouldn't be asking such a question. – MobA11y Feb 03 '15 at 20:14

2 Answers2

6

Most likely your program looks as follows:

int main(int argc, char ** argv) {
  ... // (a)
  void initpool(void);
  ...
  initpool();
  ...
}

The part denoted with (a) must contain some non-declaration statements. In older C compilers, declarations aren't allowed after the first non-declaration statement:

void foo() {
  int a;
  int b;
  foo();
  int c; // not allowed in old C
}

So, there are two possible fixes:

// 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();
  ...
}

The initpool's forward declaration really doesn't belong inside main. Why? Because you are supposed to let the compiler help you, and you shouldn't have to repeat yourself.

In terms of verbosity, the local declarations look outright silly:

// 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();
                                 }

Finally, suppose that initpool() is implemented in a separate file. Then you're free to do whatever silliness you desire. For example:

// pool.c
void initpool(void) {
  ...
}
// main.c
int main() {
  void initpool(); // a common typo
  initpool(0);     // undefined behavior, your hard drive gets formatted
}

You should have the public API of the pool component in a separate header file:

/// 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
}

Never mind that old compilers will gladly accept, for example, this:

void fun1() {
  void initpool(int);
}
void fun2() {
  void initpool(void);
}

Finally, it must be said that in C, and only in C (not C++), the following declarations are compatible, but that doesn't make it safe. The behavior is implementation defined. Such sloppiness will generate invalid assembly with stdcall, for example.

void bar();    // an unknown, fixed number of arguments
void bar(int,int,int,int);

The void bar() is akin to void bar(...) if C allowed such. Some old C compilers do indeed allow ellipsis without a preceding argument.

Thanks to Keith Thompson for forcing me to look deeper into things, and realizing how bad some compilers I use are :)

Kuba hasn't forgotten Monica
  • 95,931
  • 16
  • 151
  • 313
  • Ah, that's it. I knew variable declarations had to go at the beginning of a function but I did not know that function declarations also had to. I've moved `list = NULL;` to below the function declarations and it now compiles successfully. Thank you! – vaindil Feb 03 '15 at 19:28
  • Huh. This is actually for a class and my professor has explicitly stated that functions **must** be declared inside the function that actually calls/uses it. Your explanation makes complete sense and if I ever write my own code I will definitely do it this way, but if we don't follow the professor's instructions **exactly** then we automatically lose 25% on the assignment, no questions asked. Thank you for explaining this, I greatly appreciate it! – vaindil Feb 03 '15 at 19:35
  • 2
    @Vaindil You need your professor to read my answer. He is doing some *serious damage* by teaching the wrong thing. Feel free to have your professor contact me - kuba at mareimbrium dothere o r g – Kuba hasn't forgotten Monica Feb 03 '15 at 19:37
  • The exact wording used is: "Declare a function in the module it is invoked and not in global scope ( A calls B and does not call C; B calls C; declare B in A and C in B; C should not be declared in A), similar to variable declarations." I wish I could ask him to look here, but he definitely is not the kind of professor to care even a little bit about what others think. It's his way or the highway; I wish this weren't true. – vaindil Feb 03 '15 at 19:41
  • Why everytime someone haunts showing the power of undefined behaviour as `// undefined behavior, your hard disk gets formatted`! Has u.b. ever done this! But, really an awesome explanation! – Am_I_Helpful Feb 03 '15 at 19:46
  • 1
    @shekharsuman I could come up with an artificial example where u.b. would do just that. And I've had u.b. corrupt flash storage in a flash driver I wrote, so that's as good as having the flash storage formatted :) – Kuba hasn't forgotten Monica Feb 03 '15 at 19:50
  • `void bar(...);` is a *syntax error* in C; a variadic function must have at least one explicit parameter. `void bar();` means that `bar` takes a fixed but unspecified number and type(s) of arguments. – Keith Thompson Feb 03 '15 at 19:55
  • "*The code below will compile, in spite of there being a bug*" -- Did you try it? gcc complains about "`conflicting types for ‘initpool’`". As long as a function declaration and definition are in the same source file, the compiler will check that they're consistent, even if the declaration is inside another function. Putting function declarations inside function bodies is arguably poor style, but it's not particularly dangerous. – Keith Thompson Feb 03 '15 at 19:59
  • @Vaindil: What does "module" mean? The C standard doesn't define such a concept. I think your instructor means that a "module" is a function body; it would make more sense to apply the term "module" to a source file. – Keith Thompson Feb 03 '15 at 20:12
  • "how bad some compilers I use are " - they are just following the C89 standard! These constructs *must* be permitted. – M.M Feb 03 '15 at 21:09
  • "The `void bar()` is akin to `void bar(...) if C allowed such`" -- No, not really. Presumably `void bar(...)` would be a variadic function, which would *not* be compatible with `void bar()`. And the code you say will compile is still rejected by gcc. Again, the location of the function declaration doesn't affect whether incorrect declarations will be diagnosed. Function declarations inside function definitions aren't the problem you make them out to be. – Keith Thompson Feb 03 '15 at 22:07
  • @KeithThompson They are a problem since: 1. They force you to repeat yourself. 2. They promote a style of not having function declarations in headers. 3. Due to 2., they'll let your code have silent mistakes if you decide to split things across files. 4. There are old compilers that treat such declarations in the wrong way even if the declaration and definition are in the same file. All of the code above compiles, btw (each fun# individually). – Kuba hasn't forgotten Monica Feb 03 '15 at 22:39
  • You mean each function compiles if you isolate it into a separate source file. That's not at all clear from your answer; you have all the functions in a single block of code. Again, the location of a function declaration within a file, inside or outside a function definition, doesn't affect error checking. Most of your answer talks about reasons to avoid function declarations inside function definitions -- something that is *not* related to the OP's problem. You also discuss the drawbacks of non-prototype declarations, when there are no such declarations in the question. – Keith Thompson Feb 03 '15 at 22:45
3

Putting together the code fragments in your question, you have the following:

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

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

    /* ... */
}

Prior to the 1999 ISO C standard, C did not permit declarations and statements to be mixed within a block. Each block, including the outer block of a function definition, must contain zero or more declarations followed by zero or more statements.

The 1999 standard relaxed this rule (following C++), but many C compiler still enforce the C90 rules by default. (C90 is sometimes incorrectly called "ANSI C".)

You have a statement:

list = NULL;

followed by a declaration:

void initpool(void);

Moving the declaration above the statement should correct the problem. Using a compiler option to use the C99 or later standard should also correct the problem, but that might not be available depending on which compiler you're using. gcc has -std=c99, -std=gnu99, -std=c11, and -std=gnu11; read the gcc manual for details. I don't know what compiler "cc" is; that's a common name for a number of different C compilers.

Incidentally, putting function declarations inside a function definition is a bit unusual. It's more common to put all function declarations at file scope, or for larger projects to put declarations in a header file that's #includeed both by the .c file that defines the functions and by any .c files that contain calls to them. Apparently your instructor insists on this style, though. It's not wrong, and as long as the function declaration and definition appear in the same source file the compiler will diagnose any inconsistencies.

There is a potential problem if the declaration uses empty parentheses:

void initpool();

That is, unfortunately, compatible with:

void initpool(int n) { /* ... */ }

But that's independent of whether the declaration is inside a function body, and it's easily avoided by consistently using prototypes.

For a function that takes no arguments, use (void), not (). You're already using correct prototypes; just keep doing that.

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631