55 The world of names — four name spaces and three axes
What to know first
Looking back
Chapter 54 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?
55.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, 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.
55.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 56.1
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/ch55/four_spaces.c
/* 같은 철자 x 를 네 이름 공간에 동시에 둔다 — 표준 §6.2.3.
합법이고 컴파일된다. 읽기 좋은가는 별개의 문제다. */
#include <stdio.h>
/* ② 태그 이름 공간 — struct/union/enum 뒤에 오는 이름 */
struct x {
int x; /* ③ 멤버 이름 공간 — struct x 만의 마당 */
int y;
};
/* 다른 구조체의 멤버는 또 다른 마당이라 같은 철자를 다시 써도 된다 */
struct point { int x; int y; };
/* ④ 보통 식별자 — 변수·함수·typedef 이름·열거 상수가 모두 여기에 산다.
태그 x 와 철자가 같지만 다른 이름 공간이라 공존한다. */
typedef struct x x;
static int show(x v, struct point p)
{
/* ① 레이블 이름 공간 — goto 의 표적. 이것도 x 라 지을 수 있다 */
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: /* 레이블 x */
puts("negative, so we came to label x");
return 1;
}
int main(void)
{
/* 네 이름 공간에 x 가 하나씩 있는 상태에서 전부 쓴다 */
x a = { .x = 10, .y = 20 }; /* typedef 이름 x */
struct x b = { .x = -1, .y = 0 }; /* 태그 x */
struct point p = { .x = 3, .y = 4 };
puts("[the same spelling x lives in four name spaces at once]");
puts(" ① label x ② tag struct x ③ member x ④ typedef name x");
puts("");
(void)show(a, p);
(void)show(b, p);
/* 이름을 찾는 자리(문법적 문맥)가 이름 공간을 고른다.
struct 뒤 → 태그, . 뒤 → 멤버, goto 뒤 → 레이블, 그 밖 → 보통 식별자. */
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 four name spaces at once]
① label x ② tag struct x ③ member x ④ typedef name x
struct x : x=10 y=20
struct point: x=3 y=4
negative, so we came to 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 57). 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.
55.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 remaining independent of one another.
| Axis | What it settles | Values | What sets it |
|---|---|---|---|
| Scope (ch. 24) | where it is visible | block / file / function / function prototype | where the declaration was written |
| Linkage (ch. 54) | whether it is the same thing as that 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 56.2
One line confirms that the three are orthogonal — a file-scope struct node tag is “file scope + no linkage (tags have none) + tag name space”, while its member next is “visible only inside that struct + no linkage + member name space”.
Q. Why do tags and members have no linkage?
A. Linkage is a property of the linker’s joining of names, and tags and members never reach the linker. The name of a type and the names of the slots inside it vanish once compilation ends — what remains in machine code is an offset (chapter 47), 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 54′s discipline about headers matters.
55.4 A habit born of tags living apart
That tags and ordinary identifiers are different yards has shaped how C code looks.
examples/ch55/tag_typedef.c
/* 태그와 typedef 가 갈리는 자리, 그리고 열거 상수가 '보통 식별자'라서
생기는 충돌. */
#include <stdio.h>
/* 태그 이름 공간과 보통 식별자 이름 공간이 달라서
"같은 철자"로 태그와 typedef 이름을 둘 다 만들 수 있다 */
typedef struct node {
int value;
struct node *next; /* 태그가 있어야 자기 자신을 가리킬 수 있다 */
} node;
/* 열거 상수는 태그가 아니라 *보통 식별자* 다.
그래서 아래 red 는 int 변수 red 와 같은 마당에 산다 — 충돌한다. */
enum color { red, green, blue };
/* 이렇게 쓰면 컴파일 오류다(주석으로만 보인다):
int red; // error: 'red' redeclared as different kind of symbol
그래서 실무는 열거 상수에 접두어를 붙인다. */
enum status { STATUS_OK, STATUS_BUSY, STATUS_FAIL };
/* 반대로 태그는 보통 식별자와 절대 충돌하지 않는다.
단, struct/union/enum 은 태그 마당을 *셋이서 함께 쓴다* — 그래서
struct status { int code; };
는 위의 enum status 와 충돌한다("defined as wrong kind of tag").
태그 마당은 넷 중 하나이지, 키워드마다 하나가 아니다. */
struct handle { int code; }; /* 다른 철자를 쓴다 */
/* 반면 보통 식별자 마당은 태그와 완전히 별개다 — 같은 철자를 써도 된다 */
static int status = 42; /* enum status 태그와 공존한다 */
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("[tags and typedef names are different name spaces, so the spelling can be shared]");
for (node *p = &a; p; p = p->next)
printf(" node %d\n", p->value);
puts("\n[enumeration constants are ordinary identifiers - the same space as variables]");
printf(" enum color: %s %s %s\n", name_of(red), name_of(green), name_of(blue));
puts(" which is why int red; is a compile error - hence the habit of prefixes (STATUS_OK)");
puts("\n[struct, union and enum share one tag space - 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(" you cannot declare struct status - enum status already took that tag");
puts("\n[but ordinary identifiers live in a different space from tags]");
printf(" the variable status = %d lives beside the tag status\n", status);
return 0;
}
Output
[tags and typedef names are different name spaces, so the spelling can be shared]
node 1
node 2
[enumeration constants are ordinary identifiers - the same space as variables]
enum color: red green blue
which is why int red; is a compile error - hence the habit of prefixes (STATUS_OK)
[struct, union and enum share one tag space - there is only one]
struct handle.code = 7, enum status = 1
you cannot declare struct status - enum status already took that tag
[but ordinary identifiers live in a different space from tags]
the variable status = 42 lives beside the tag status
55.4.1 typedef struct node node;
The commonest idiom. The tag node and the typedef name node are different yards, so the same spelling serves both, and the caller 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.
55.4.2 And yet there are conventions that forbid typedef
The Linux kernel’s coding style is the well-known one. Its ground is 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 56.3
Which of the three is right is not settled. Settling on one and keeping it is.
55.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 with 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.
55.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 not one shadowing (only the unused variable) |
-Wshadow | catches all three — the parameter over the global, and both nested locals |
Table 56.4
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 and Clang’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.
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 the pattern -Wshadow is best at catching.
55.7 Names you must not use — the reserved yard
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 (ch. 12) |
Table 56.5
The ground is §7.1.3, and the fuller story returns in chapter 82.
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 12′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 inside your own yard, 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 this spot.