2

When there is no visible declaration or definition of struct bar in the following code, it compiles successfully as C++ but not as C:

void foo(struct bar* p); void foo(struct bar* p){} int main(){} 

Error message when compiled as C: error: conflicting types for 'foo'.

Can anyone explain this behaviour?

I have tried this with both clang++ 3.4 and g++ 4.8.2 with -Wall -Wextra -pedantic-errors flags and either -std=c99 or -std=c++03 for C and C++ respectively.

9
  • Is struct bar defined above, in between, or below these lines (or possibly not at all)? Commented Oct 26, 2014 at 13:12
  • @Jongware Not at all, of course. Commented Oct 26, 2014 at 13:13
  • 1
    Adding a definition for struct bar makes this error go away. Is your question "why is a not-defined type not equal to the same not-defined type"? Commented Oct 26, 2014 at 13:21
  • @Jongware Possibly it is. Possibly it is not. I don't know is it valid to use not-defined types in the same manner. Commented Oct 26, 2014 at 13:24
  • If your question is commented on or attracts down-votes or close requests, it is a hint to improve it! There's a legitimate and interesting question here. If you are lucky, my edits have saved it from oblivion. However you might want to check that I have not just changed the question. BTW if two different, widely used and mature compilers do the same thing - a compiler bug is the least likely explanation; this one is clearly about the different semantics of the same code in C and C++. Commented Oct 26, 2014 at 14:27

1 Answer 1

10

Let's simplify the program by omitting the declaration and the useless main:

void foo(struct bar* p){} 

The compiler sees struct bar, which has not been defined. The error message from GCC 4.8.2 explains what it does next:

a.c:1:17: warning: ‘struct bar’ declared inside parameter list [enabled by default] void foo(struct bar* p){} ^ a.c:1:17: warning: its scope is only this definition or declaration, which is probably not what you want [enabled by default] 

So now it assumes that struct bar is something that only exists within the definition of foo. The code compiles perfectly, though.

When you add the function prototype:

void foo(struct bar* p); void foo(struct bar* p){} 

the warning becomes:

a.c:1:17: warning: ‘struct bar’ declared inside parameter list [enabled by default] void foo(struct bar* p); ^ a.c:1:17: warning: its scope is only this definition or declaration, which is probably not what you want [enabled by default] a.c:3:17: warning: ‘struct bar’ declared inside parameter list [enabled by default] void foo(struct bar* p){} ^ a.c:3:6: error: conflicting types for ‘foo’ void foo(struct bar* p){} ^ a.c:1:6: note: previous declaration of ‘foo’ was here void foo(struct bar* p); ^ 

So like before, the compiler makes up a new, undefined type struct bar for the prototype, and another one for the function definition. So the prototype for foo and its definition refer to different types, both named struct bar. They don't match, hence the error.

The solution is to first forward-declare the struct:

struct bar; void foo(struct bar* p); void foo(struct bar* p){} 

This compiles without warnings.

Sign up to request clarification or add additional context in comments.

5 Comments

OK. But why does the same code compile successfully with clang++ 3.4 and g++ 4.8.2 (with -Wall -Wextra -pedantic-errors -std=c++03 flags)?
@Constructor Because C++ apparently has different rules for struct definition scope. Sorry, missed that bit in your question, and I can't quote the C++ standard for the rules.
There is no reason to apologize. I've added this piece of the question after your answer. It is sadly that you haven't a relevant quote from the C++ standard. But what about the C one?
@Constructor; C11 6.2.1 defines scope, especially p4 and p7 is relevant (function-prototype scope is what you're after). I currently fail to find the part where it's mandated that the forward-declaration works (and struct bar in the prototype doesn't declare a new type and hide the outer definition), though.
@mafso : Surely it works in C++ is because the first use is an implicit forward declaration and there is only one declaration? That being the case, there would not be an explicit mention regarding forward declaration overriding the implied declaration, because they were always the same. As the C warning says the behaviour is "probably not what you want", so in C++ it is fixed, probably because in C++'s design, compatibility with C was secondary to fixing some of C's semantic flaws.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.