Conflicting Declarations in Same Scope
When extern int x;
appears outside a function, it declares x
at file scope. Then, when int x;
appears inside the function, it declares a new instance of x
that is unrelated to the earlier extern int x;
. This is allowed by the C language, so the compiler does not complain about it.
When extern int x;
appears inside the function, it declares x
at block scope. Then, when int x;
appears after it, it attempts to declare a different x
in the same scope. This is not allowed by the C standard, so the compiler reports an error.
The extern
keyword is not particularly relevant here—the error is caused by the fact that there are two conflicting declarations of the same identifier. For example:
char c;
int main(void)
{
char d;
int c; // Allowed, new declaration in new scope.
int d; // Not allowed, conflicting declaration in same scope.
}
Rules About Declarations
The rules about C declarations have some irregularities due to the history of C development. Consider these declarations at file scope:
extern int x;
int x;
The first declaration does (or does not do) several things:
- It says
x
is an identifier for an int
.
- It says
x
has external linkage, meaning it can be made (during linking of the object modules) to refer to an object named x
declared somewhere else.
- It does not define an
int
.
For the second declaration:
- It says
x
is an identifier for an int
.
- It says
x
has external linkage (because external is the default for declarations for objects without a storage class specifier like static
outside functions).
- It defines an
int
. (It is actually a tentative definition, but we will not deal with that here.)
Both of these declarations say x
is an identifier for an int
and has external linkage. The difference between them is the first does not define an object (it merely says x
is a name for an object defined somewhere else) and the second does define an int
(so it is the somewhere else). So these declarations do not conflict and are allowed.
On other hand, consider these same declarations inside a function. Then they are at block scope.
Then extern int x;
has the same meaning as above: x
is an identifier with external linkage for an object defined elsewhere.
But int x;
has a different meaning. Instead of saying x
has external (or internal) linkage, it says x
has no linkage, because no linkage is the default for declarations in block scope. This creates a conflict because C 2018 6.7 3 says an identifier with no linkage shall not be declared more than once in the same scope (and name space, not addressed here) except for typedef
names and tags with certain conditions.
Scopes
C has four kinds of scopes:
- File scope is for declarations outside of functions and lasts to the end of the source file being compiled.
- Block scope is for declarations inside functions and lasts until the end of a block (discussed below).
- Function prototype scope is for declarations in the parameters of function prototypes. (For example, in
void foo(int n, float a[n][n]);
, n
and a
have function prototype scope.)
- Function scope is for labels to be used in
goto
statements.
A compound statement is a list of declarations and statements inside {
and }
. Each compound statement is a block, which creates a new scope for declarations. The main body of a function is a compound statement that is a block, and it may have additional compound statements inside it, each starting a new scope.
Blocks are also created by switch
, if
, do
, while
, and for
statements, but they are largely unimportant for the first four of those, as only the for
statement provides an opportunity for further declarations. For example, in a for
statement, you can write for (int x = 3; x < 20; ++x)
, and that creates a new instance of x
because the for
statement starts a new block.