What name space is a typedef name in? Consider this code:

#include <stdio.h>

typedef struct x {   // 'x' in tag name space
   int x;            // 'x' in member name space
   int y;
} x;                 // ??

int main() {
   x foo = { 1, 2 };
   int x = 3;        // 'x' in ordinary identifier name space

   printf("%d %d %d\n", foo.x, foo.y, x);
}

This prints out '1 2 3' with gcc 4.4.7 (and g++ 4.4.7), so type names are separate from tag, member, and ordinary identifier names.

This code also compiles and runs on gcc/g++ 4.4.7, producing '1, 2':

#include <stdio.h>

typedef struct x {   // 'x' in tag namespace
   int x;            // 'x' in member namespace
   int y;
} x;

int main() {
   x x = { 1, 2 };

   printf("%d %d\n", x.x, x.y);
}

How are the x identifiers disambiguated in this case?

EDIT A clarification, I hope. Consider these two lines from above:

   x foo = { 1, 2 };
   int x = 3;        // 'x' in ordinary identifier name space

When the second line is executed, identifier x is in scope, and should logically be in the 'ordinary identifier' namespace. There doesn't appear to be a new scope at this point, because there is no opening brace between lines 1 and 2. So the second x can't hide the first x, and the second x is in error. What's the flaw in this argument, and how does this apply to the x x case? My assumption was that the flaw was that type names somehow had a different non-obvious name space, hence the title of this question.

1 Answers

4
StoryTeller On Best Solutions

It works not due to namespaces (the new type name and the variable identifier are in the same ordinary namespace), but due to scoping.

6.2.1 Scopes of identifiers

2 For each different entity that an identifier designates, the identifier is visible (i.e., can be used) only within a region of program text called its scope. Different entities designated by the same identifier either have different scopes, or are in different name spaces. There are four kinds of scopes: function, file, block, and function prototype. (A function prototype is a declaration of a function that declares the types of its parameters.)

4 Every other identifier has scope determined by the placement of its declaration (in a declarator or type specifier). If the declarator or type specifier that declares the identifier appears outside of any block or list of parameters, the identifier has file scope, which terminates at the end of the translation unit. If the declarator or type specifier that declares the identifier appears inside a block or within the list of parameter declarations in a function definition, the identifier has block scope, which terminates at the end of the associated block. If the declarator or type specifier that declares the identifier appears within the list of parameter declarations in a function prototype (not part of a function definition), the identifier has function prototype scope, which terminates at the end of the function declarator. If an identifier designates two different entities in the same name space, the scopes might overlap. If so, the scope of one entity (the inner scope) will end strictly before the scope of the other entity (the outer scope). Within the inner scope, the identifier designates the entity declared in the inner scope; the entity declared in the outer scope is hidden (and not visible) within the inner scope.

The variable named x is an an inner scope. So it hides the entity named x in the outer scope. Inside the scope of main, following the declaration of x x, it's a variable name.

The interesting bit is that in x x = { 1, 2 }; the meaning of x is changed mid declaration. In the beginning it denotes the type name, but once the declarator introduces an identifier, x starts denoting the variable.


Regarding your edit "What's the flaw in this argument?" Note that scopes may overlap (as the preceding paragraph mentions). The definition of the type alias is actually at file scope. The block scope of main is a new inner scope that overlaps the outer scope. That's why it can be used to hide the previous meaning of x. Had you tried to do this at file scope:

typedef struct x { /* ... */ } x;
int x = 1; // immediately at file scope

It would be ill-formed. Because now indeed the declarations appear at the exact same scope.