63 The world of names — four name spaces and three axes
What to know first
Looking back
Chapter 60 said a name has, besides scope, a second property called linkage. Then why does the following compile? struct node and node are the same spelling and yet do not collide.
typedef struct node node;A. Because there is a third property that is neither scope nor linkage — the name space. The compiler looks up a name that follows struct and a name used plainly in different lists. Same spelling, different list, no collision.
This chapter takes on how many such lists there are and what they hold, and how they differ from scope and linkage.
The need for this chapter, and its context
By the end of this chapter
The questions this chapter answers
- Which name space do macros live in?
- Why do tags and members have no linkage?
- Should
-Wshadowthen always be on?
63.1 The truth behind “C has no name spaces”#
The saying is widespread, and it misleads just as widely. The standard’s own sentence says nearly the opposite.
Section 6.2.3 states that if more than one declaration of a particular identifier is visible at a point in a translation unit, the syntactic context tells the uses apart; thus there are separate name spaces for the various categories of identifiers.
So C does have name spaces. What it lacks is the ability to open a new yard (this chapter calls each name space a yard where names live), as namespace app { … } does. Blur that distinction and you cannot explain why both of the following are true.
typedef struct node node;is legal — different name spaces.- Two libraries that each export a function called
initbreak the link — the same name space.
A common misconception. “C has no concept of a name space at all”
Section 6.2.3 names four of them explicitly. The accurate statement is “C has no user-defined name spaces.”
The misconception costs something in practice. Believing there are none, you cannot explain what happens in the POSIX headers where struct stat and the function stat live side by side, nor why an enumeration constant collides with a variable. “There are four, and I cannot make a fifth” is the accurate guide.
63.2 The four name spaces#
| Name space | What lives here | Which syntactic slot looks it up |
|---|---|---|
| the targets of goto | after goto, and name: before a statement |
| the name after struct, union, enum | immediately after those keywords |
| members of a struct or union — one yard per type | after . and -> |
| variables, functions, typedef names, enumeration constants, parameters | everywhere else |
Table 63.1 — The four name spaces and what lives in each
Three points are worth fixing in mind.
- Members have a yard per type. The
xofstruct pointand thexofstruct vechave nothing to do with one another. That is why member names can stay short however many structs there are. - There is one tag yard.
struct,unionandenumshare it between the three of them. So ifenum statusexists,struct statuscannot be made. - Enumeration constants live in 4, not with the tags. This is where practice stubs its toe most often.
examples-en/name-spaces/four_spaces.c
/* The same spelling x placed in all four name spaces at once — §6.2.3.
It is legal and it compiles. Whether it reads well is another matter. */
#include <stdio.h>
/* (2) the tag name space — the name that follows struct/union/enum */
struct x {
int x; /* (3) the member name space — a yard of struct x's own */
int y;
};
/* Another struct's members are another yard, so the spelling may recur */
struct point { int x; int y; };
/* (4) ordinary identifiers — variables, functions, typedef names and
enumeration constants all live here. Same spelling as the tag x,
different name space, so they coexist. */
typedef struct x x;
static int show(x v, struct point p)
{
/* (1) the label name space — goto's target. This may be called x too */
if (v.x < 0) goto x;
printf("struct x : x=%d y=%d\n", v.x, v.y);
printf("struct point: x=%d y=%d\n", p.x, p.y);
return 0;
x: /* the label x */
puts("negative, so we came to the label x");
return 1;
}
int main(void)
{
/* With one x in each of the four name spaces, use them all */
x a = { .x = 10, .y = 20 }; /* the typedef name x */
struct x b = { .x = -1, .y = 0 }; /* the tag x */
struct point p = { .x = 3, .y = 4 };
puts("[the same spelling x lives in all four name spaces at once]");
puts(" (1) label x (2) tag struct x (3) member x (4) typedef name x");
puts("");
(void)show(a, p);
(void)show(b, p);
/* The syntactic context picks the name space:
after struct -> tag, after . -> member, after goto -> label,
anywhere else -> ordinary identifier. */
printf("\nsizeof(x) = %zu, sizeof(struct x) = %zu (the same type)\n",
sizeof(x), sizeof(struct x));
return 0;
}
Output
[the same spelling x lives in all four name spaces at once]
(1) label x (2) tag struct x (3) member x (4) typedef name x
struct x : x=10 y=20
struct point: x=3 y=4
negative, so we came to the label x
sizeof(x) = 8, sizeof(struct x) = 8 (the same type)
The same spelling x can live in all four yards at once and still compile and run, because the compiler picks which yard to search by syntactic context — after goto a label, after struct a tag, after . a member, elsewhere an ordinary identifier.
This is not, of course, an invitation to write that way. What the language allows and what a person can read are different ranges, and this listing goes to the extreme deliberately to show the first.
Q. Which name space do macros live in?
A. None. A macro is a name of the preprocessor, not of the language, and the preprocessor knows nothing of C’s grammar (chapter 65). So a macro name ignores all four yards and replaces tokens wherever they appear.
#define max 100
struct max { int x; }; // after struct, yet replaced by 100 → errorMacros are the one place the name spaces cannot protect, which is exactly why the habit of writing macro names in capitals became so firmly fixed. A fence the grammar cannot build is built by notation instead.
63.3 Three axes — scope, linkage, name space#
Now put the three side by side. They confuse people because all three attach to the same name at once while each answers a different question.
| Axis | What it settles | Values | What sets it |
|---|---|---|---|
| Scope (chapter 62) | where it is visible | block / file / function / function prototype | where the declaration was written |
| Linkage (chapter 60) | whether it is the same thing as the same spelling in another translation unit | external / internal / none | static, extern, and where it is declared |
| Name space (here) | which list it is looked up in | label / tag / member / ordinary | the syntactic slot |
Table 63.2 — The three axes that settle a name
The three answer different questions — so each must be asked separately. They are not independent, though. Where a declaration sits settles both its scope and its linkage: move a file-scope int n; into a block and its scope goes from file to block while its linkage goes from external to none. And tags, members and labels never have linkage. One example reads the three apart: a file-scope struct node tag is “file scope + no linkage (tags have none) + tag name space”, while its member next is “file scope + no linkage + member name space”. The two share scope and linkage and differ only in name space — that next is looked up only after . or -> is the third axis’s doing.
Q. Why do tags and members have no linkage?
A. Linkage is a property of how the linker joins names, and tags and members never reach the linker. The name of a type and the names of its members vanish once compilation ends — what remains in machine code is an offset (chapter 48), not a name.
So two files may each declare struct point and the link is fine. In exchange a danger appears: if the two declarations differ, nobody says a word. That is why chapter 60′s discipline about headers matters.
63.4 A habit born of tags living apart#
That tags and ordinary identifiers are different yards has shaped how C code looks.
examples-en/name-spaces/tag_typedef.c
/* Where tag and typedef part ways, and the collision that arises because
enumeration constants are ordinary identifiers. */
#include <stdio.h>
/* The tag name space and the ordinary-identifier name space differ, so the
same spelling can name both a tag and a typedef */
typedef struct node {
int value;
struct node *next; /* the tag is what lets it point at itself */
} node;
/* Enumeration constants are ordinary identifiers, not tags.
So red below lives in the same yard as an int variable red — they clash. */
enum color { red, green, blue };
/* Written out, this is a compile error (shown only as a comment):
int red; // error: 'red' redeclared as different kind of symbol
Which is why practice puts a prefix on enumeration constants. */
enum status { STATUS_OK, STATUS_BUSY, STATUS_FAIL };
/* Conversely a tag never clashes with an ordinary identifier.
But struct/union/enum *share one tag yard between them* — so
struct status { int code; };
clashes with the enum status above ("defined as wrong kind of tag").
There is one tag name space, not one per keyword. */
struct handle { int code; }; /* use a different spelling */
/* The ordinary-identifier yard, though, is wholly separate from the tag one */
static int status = 42; /* coexists with the enum status tag */
static const char *name_of(enum color c)
{
switch (c) {
case red: return "red";
case green: return "green";
case blue: return "blue";
}
return "?";
}
int main(void)
{
node b = { .value = 2, .next = nullptr };
node a = { .value = 1, .next = &b };
puts("[tag and typedef name are different name spaces, so one spelling serves]");
for (node *p = &a; p; p = p->next)
printf(" node %d\n", p->value);
puts("\n[enumeration constants are ordinary identifiers — the variables' yard]");
printf(" enum color: %s %s %s\n", name_of(red), name_of(green), name_of(blue));
puts(" so int red; is a compile error — hence the STATUS_OK prefix habit");
puts("\n[struct, union and enum share the tag yard — there is only one]");
struct handle h = { .code = 7 };
enum status e = STATUS_BUSY;
printf(" struct handle.code = %d, enum status = %d\n", h.code, (int)e);
puts(" struct status is impossible — enum status already took that tag");
puts("\n[but ordinary identifiers sit in a different yard from tags]");
printf(" the variable status = %d lives beside the tag status\n", status);
return 0;
}
Output
[tag and typedef name are different name spaces, so one spelling serves]
node 1
node 2
[enumeration constants are ordinary identifiers — the variables' yard]
enum color: red green blue
so int red; is a compile error — hence the STATUS_OK prefix habit
[struct, union and enum share the tag yard — there is only one]
struct handle.code = 7, enum status = 1
struct status is impossible — enum status already took that tag
[but ordinary identifiers sit in a different yard from tags]
the variable status = 42 lives beside the tag status
63.4.1 typedef struct node node;#
The commonest idiom. The tag node and the typedef name node are in different yards, so the same spelling serves both, and code using the type need not write struct every time. Notice too that a pointer to itself requires the tag — at the point struct node *next; is written, the typedef name is not yet complete.
63.4.2 And yet there are conventions that forbid typedef#
The Linux kernel’s coding style is the well-known one. Its objection is the concealment of information — node n; alone does not say whether that is a struct, a pointer or an integer, whereas struct node n; says so on sight. Where large code is read by many, that information is worth more than a few keystrokes.
| Policy | Ground | Where it is used |
|---|---|---|
| Tag and typedef share a spelling | the calling side gets shorter | public library APIs (SQLite, SDL, …) |
No typedef at all | seeing struct reveals the nature | the Linux kernel and kernel-like projects |
typedef only for opaque types | hide what is hidden, open what is open | handle-passing APIs (FILE * is the archetype) |
Table 63.3 — Naming policies and their grounds
Which of the three is right is not settled. Settling on one and keeping it is.
63.5 Enumeration constants are ordinary identifiers#
Write enum color { red, green, blue }; and red, green and blue go not into the tag yard but into yard 4 — the same yard as variables and functions.
enum color { red, green, blue };
int red; // error: 'red' redeclared as different kind of symbolHence practice’s convention of prefixing enumeration constants — STATUS_OK, GTK_ALIGN_FILL, SDL_QUIT. The names look long, but in a world where variables, functions and enumeration constants share one yard that prefix is the fence.
Platform note. C++‘s enum class
C++11 parted ways here. Declare enum class color { red }; and the constants do not leak into the surrounding yard; they are written color::red only — “one yard per enumeration”.
C23 introduced syntax for an enumeration’s underlying type (enum color : unsigned char { … }) but left the yard the constants live in alone. The two features sound alike and solve different problems — one is about size, the other about names.
63.6 Shadowing#
Declare the same spelling again in an inner scope and the outer one is shadowed. It is legal, occasionally useful, and often the site of an accident.
int count = 1; // global
static int f(int count) { // the parameter shadows the global
{ int x = count; { int x = 2; return x; } } // inner x shadows outer x
}The trouble is that the warning is not on by default.
| Option | Measured (GCC 14) |
|---|---|
-Wall -Wextra | catches no shadowing at all (only the unused variable) |
-Wshadow | catches both — the parameter over the global, and the inner x over the outer one |
Table 63.4 — Options that catch shadowing, measured
Q. Should -Wshadow then always be on?
A. For new code, yes. Turn it on for the first time in old code and warnings pour out; there are compromises for that moment.
- GCC’s
-Wshadow=local(only a local shadowing a local) or-Wshadow=compatible-local(only when the types match too). -Wshadow=globalto catch only what shadows a global.- These three are GCC options. Clang 22.1 calls
-Wshadow=localan “unknown warning option” and offers-Wshadowand the wider-Wshadow-allinstead.
The root cure is elsewhere — have fewer globals. With no global to shadow there is no shadowing accident. The next chapter’s “export only what you must” is the same cure with another face.
Counter-example. Re-declaring the loop variable inside
for (int i = 0; i < n; i++) {
for (int i = 0; i < m; i++) { /* the outer i is lost */ }
}It compiles. But there is no way to refer to the outer i from the inner block, and the day somebody lifts the inner loop’s body into a function, the meaning changes quietly. This is a typical pattern that -Wshadow catches.
63.7 Names you must not use — names reserved for the standard and the implementation#
In a world with only four name spaces, the standard library and the implementation reserved space in advance. Trespass and it may work today and break in the next version.
| What is reserved | Where | Example |
|---|---|---|
| underscore + capital, and two underscores | for any use, always | _Value, __x, _Atomic |
| one underscore + lowercase | file-scope ordinary identifiers and tags | _helper (not as a global) |
str, mem, wcs + lowercase | taken by the <string.h> family | strdup, memcpy2 |
is, to + lowercase | the <ctype.h> family | isodd, tolower2 |
E + capital or digit | <errno.h> | EMYERROR |
LC_ + capitals | <locale.h> | LC_MINE |
SIG, SIG_ + capitals | <signal.h> | SIGMINE |
PRI, SCN + lowercase | <inttypes.h> | PRIxmine |
the _t suffix | reserved by POSIX (not by standard C) | mytype_t — a grey area (chapter 13) |
Table 63.5 — The range of reserved names
The ground is §7.1.3, and the fuller story returns in chapter 97.
★ C23 drew one more mark here — the potentially reserved identifier. Names the standard describes as optional (an implementation may or may not provide them) are not reserved until provided, but sit where an implementation or a future edition may take them at any time. Before C23 the line between reserved and not was blurred and easy to slip past into undefined behaviour; naming the case split it cleanly.
The practical meaning is one sentence — such a name may work today and not tomorrow. The standard even encourages implementations to diagnose their use (§7.1.3).
In practice. Why the _t suffix is a grey area
Since standard types end in _t — size_t, uint32_t — you want to put it on your own. But standard C did not reserve _t, and POSIX did.
So the situation splits. In a pure standard-C program mytype_t is legal. Let that same code meet a POSIX system’s headers and it may collide the day POSIX starts using that name — which has genuinely happened more than once.
Chapter 13′s ladder makes the judgement simple. For code only you use, one comment suffices; for a library shipping to several platforms, put a prefix in front and keep it under your own prefix, as prov_str_t does. That is why most large projects chose the latter.
We know the rules for names. Yet keep every rule and two libraries exporting the same spelling still collide — there is no way to dig a new yard in C. That is the next chapter’s problem: how collisions are prevented, and how C++ solved it.