Proven C Book←↑→

63 The world of names — four name spaces and three axes

What to know first

chapter 62, Where a name may be used — scope · scope — where a name is visible
chapter 60, Building out of several files · linkage — does a name cross the file boundary

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

The moment files are split, names start colliding. So name spaces come after linking (chapter 60) and scope (chapter 62) — inside one file, scope was enough, but the instant a boundary is crossed, linkage appears as one more axis. With name spaces, this chapter’s subject, there are now three axes in play, and this is the place to erect all three at once.

By the end of this chapter

You often hear that “C has no name spaces”. It is half true — the standard defines four of them. What C lacks is a way for you to make a new one. This chapter sets out the four, binds them together with scope and linkage into three axes, and covers shadowing and reserved names. The next chapter builds on it to answer “so how do collisions get prevented”.

The questions this chapter answers

  1. Which name space do macros live in?
  2. Why do tags and members have no linkage?
  3. Should -Wshadow then 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.

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 spaceWhat lives hereWhich syntactic slot looks it up
  1. Labels
the targets of gotoafter goto, and name: before a statement
  1. Tags
the name after struct, union, enumimmediately after those keywords
  1. Members
members of a struct or union — one yard per typeafter . and ->
  1. Ordinary identifiers
variables, functions, typedef names, enumeration constants, parameterseverywhere else

Table 63.1 — The four name spaces and what lives in each

Three points are worth fixing in mind.

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 → error

Macros 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.

AxisWhat it settlesValuesWhat sets it
Scope (chapter 62)where it is visibleblock / file / function / function prototypewhere the declaration was written
Linkage (chapter 60)whether it is the same thing as the same spelling in another translation unitexternal / internal / nonestatic, extern, and where it is declared
Name space (here)which list it is looked up inlabel / tag / member / ordinarythe 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.

PolicyGroundWhere it is used
Tag and typedef share a spellingthe calling side gets shorterpublic library APIs (SQLite, SDL, …)
No typedef at allseeing struct reveals the naturethe Linux kernel and kernel-like projects
typedef only for opaque typeshide what is hidden, open what is openhandle-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 symbol

Hence 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.

OptionMeasured (GCC 14)
-Wall -Wextracatches no shadowing at all (only the unused variable)
-Wshadowcatches 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=global to catch only what shadows a global.
  • These three are GCC options. Clang 22.1 calls -Wshadow=local an “unknown warning option” and offers -Wshadow and the wider -Wshadow-all instead.

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 reservedWhereExample
underscore + capital, and two underscoresfor any use, always_Value, __x, _Atomic
one underscore + lowercasefile-scope ordinary identifiers and tags_helper (not as a global)
str, mem, wcs + lowercasetaken by the <string.h> familystrdup, memcpy2
is, to + lowercasethe <ctype.h> familyisodd, 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 suffixreserved 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.