I recently stumbled on a weird code construct that lead the C compilers to a strange state. I would like to have an explanation why it occurs.
Here is my small code snippet that demonstrate the problem:
#include <stdlib.h>
typedef int type_t;
int main (void)
{
int a = 10, b = 100;
type_t *type_t = &a; // We name a variable with type name
type_t *type_c = &b; // We define a variable with type_t
return EXIT_SUCCESS;
}
And here is the error message displayed by gcc
:
#> gcc -Wall -Wextra -o sample sample.c
sample.c: In function ‘main’:
sample.c:11:11: error: ‘type_c’ undeclared (first use in this function); did you mean ‘type_t’?
type_t *type_c = &b;
^~~~~~
type_t
sample.c:11:11: note: each undeclared identifier is reported only once for each function it appears in
Or, with clang
:
#> clang -Wall -Wextra -o sample sample.c
sample.c:11:11: error: use of undeclared identifier 'type_c'; did you mean 'type_t'?
type_t *type_c = &b;
^~~~~~
type_t
sample.c:10:11: note: 'type_t' declared here
type_t *type_t = &a;
^
sample.c:11:10: error: invalid operands to binary expression ('type_t *' (aka 'int *') and 'type_t *')
type_t *type_c = &b;
~~~~~~ ^~~~~~~
2 errors generated.
Note that if we modify the code as follow, it compiles fine:
int main (void)
{
int a = 10, b = 100;
type_t *type_c = &b; // We define a variable with type_t
type_t *type_t = &a; // We name a variable with type name
return EXIT_SUCCESS;
}
So, my question now!
It seems that the error is produced by the fact that the l-value of the assignation operator '=' is mistaken as a multiplication between type_t
and type_c
. As type_c
is unknown, it explains the error message.
But, why do we have this confusion on an l-value ? Should not it be unambiguous that the type_t
refer to the type and not the variable ?
I guess this is not an implementation problem as, both, gcc
and clang
behave the same. But, I really would like to have the key of this problem.
This is expected and correct behaviour. In C, there are a number of namespaces: labels, members, tags, and ordinary identifiers. A typedef
name is in the ordinary identifiers name space, as are variable names.
You have:
type_t *type_t = &a; // We name a variable with type name
type_t *type_c = &b; // We define a variable with type_t
There are three type_t
occurrences here. The first is the typedef name; no problem. The second is a new variable name; no problem, but it hides (shadows) the typedef name; you can no longer refer to the type type_t
in the current block of code. The third occurrence refers to the variable; you are multiplying an integer pointer by an undefined variable (and trying to use the result as an l-value to receive the address of b
), which is wrong on many levels.
When you reverse the order of those lines, it works fine because type_c
is declared to be a type_t *
OK; then type_t
is defined to be a variable of type type_t *
, but no further references can be made to the type type_t
in the current block (any such reference is to the variable, not the type).
Note that if the typedef
was inside the function, you'd not be able to shadow it with type_t *type_t = &a;
. See C11 §6.2.1 Scopes of identifiers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With