Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conflicting type and variable naming in C

Tags:

c

gcc

clang

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.

like image 455
perror Avatar asked Nov 27 '18 20:11

perror


1 Answers

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.

like image 183
Jonathan Leffler Avatar answered Nov 09 '22 06:11

Jonathan Leffler