The VikiD Truth

Knowledge base / Computing / C & systems languages

Identifiers in C

Here is the truth about names in C: a name is not one thing. The same spelling can mean four different things at once, and the compiler keeps them in separate drawers. Most “why does this even compile?” moments come from not knowing which drawer you are in.

What it is, in plain words

An identifier is a name you invent: for a variable, a function, a type tag, a struct member, a label, an enumeration constant or a macro. The rules for spelling are simple:

Names beginning with an underscore followed by a capital letter (_Bool, _Foo) or with two underscores (__x) are reserved for the implementation. Do not use them.

Every identifier has three properties the rest of this page explains: its namespace (which drawer), its scope (where in the text it is visible) and its linkage (whether the same name in another file refers to the same thing).

Why it matters

Large C programs are many files glued together by names. Clashing names, accidentally shadowed variables and globals that leak between files are some of the hardest bugs to see, because the code looks right. Understanding namespaces and scope also lets you read idioms such as struct stat stat; without flinching.

An everyday analogy: names in a building

In an office block “Ali” can be a person in reception, a meeting room called Ali, and the Ali project on the whiteboard. Nobody gets confused because context tells you which list to look in (namespace). A visitor badge only works on certain floors (scope). And a company-wide “Ali” on the staff directory is the same person in every branch, while a nickname used only in one team is not (linkage).

A hands-on example

#include <stdio.h>

struct tag { int tag; };          /* tag namespace AND member namespace */

static int hidden = 1;            /* file scope, internal linkage */
int shared = 2;                   /* file scope, external linkage */

int count(struct tag *tag) {      /* parameter 'tag': ordinary namespace */
    int shared = 10;              /* shadows the global inside this block */
    if (tag->tag > 0)
        goto done;                /* label: function scope */
    shared++;
done:
    return tag->tag + shared + hidden;
}

int main(void) {
    struct tag t = { .tag = 5 };
    printf("%d\n", count(&t));    /* prints 16 */
}

Compile with gcc -Wall -Wextra -Wshadow and GCC points out the shadowed shared. -Wshadow is not in -Wall; I add it to every project.

To see which names a compiled file exports, use nm:

gcc -c ident.c && nm ident.o
# T count   T main   D shared   d hidden

Capital letters are external linkage, lower case internal. hidden stays private to the file because of static.

Back to Tokens | Back to C Programming | The Compiler

There are 4 types of Identifiers and each type gets its own namespace.

struct tag { char tag; ...}

Here you can see we have created a structure with the tag tag, which also has a member tag of the type char.

int count( struct tag *tag)
{
 int len = length( tag->tag)
}

Here you can see we defined the function count with a parameter named tag which is a pointer to struct tag.

In all cases the name tag is in different namespaces and do not conflict.

Identifier Scope

Scopes can confusing, so lets hope we can putthe confusion to rest here.

The scope of an identifier generally begins after the declaration except when we define structures, union and enumeration types.

In these cases, the identifier goes into scope immediately, within the declaration itself. So you can used the identifier for recursive definitions.

File Scope

If you declare the identifier outside of all blocks and parameter lists. you can use the identifier anywhere within the translation unit / module.

Block Scope

Ignoring lables.

An Identifier declared in a block has the scope of the smallest block it is declared in from its location to the end of the block.

The parameters in the function also have block scope.

Function Prototype Scope

Identifiers within parameters of the prototype only have scope in the prototype.

Function Scope

Labels are special. They take the full scope of the function, no matter if they are nested within blocks.

i.e. you can use them as goto statements.

Linkage, briefly

Storage classes and linkage together are the most confusing corner of C for beginners.

Common misconceptions

Connections

Last edited · 23 revisions

Tags: #Computing