Proven C Book←↑→

58 Where a name may be used — scope

What to know first

chapter 25, Function declarations and definitions · blocks and parameters
chapter 45, Lifetime and storage duration · how long the object lives, not the name
chapter 56, Building out of several files · linkage — does a name cross file boundaries

Looking back

Chapter 25 said “a name declared inside a block is visible only inside that block”. How many names called n are there in this code?

for (int n = 0; n < 3; n++) { }
for (int n = 10; n > 8; n--) { }

A. Two, and they have nothing to do with each other. Sameness of spelling is not sameness of name: two names are the same only when they are the same spelling in the same region. This chapter chases that region to the end — how many kinds there are, where each begins and ends, and what happens where they overlap.

The need for this chapter, and its context

Scope has arrived in pieces so far — from functions to blocks, from loops to the variable in the first slot, from several files to linkage. Each of those places could say only as much as it needed. Now, just before entering the world of names, is the time to gather what was scattered and state it precisely. The name spaces of the next chapter and the collisions of the one after are spoken in this chapter’s vocabulary.

By the end of this chapter

We take the four scopes the standard defines one at a time, then ask where a scope begins (not at the equals sign). What shadowing is, why it is dangerous, and why it is nonetheless not forbidden; the exception that labels are; and the decisive contrast that the preprocessor knows nothing of scope. Finally we set scope, lifetime and linkage side by side — three words that are constantly confused for one another.

The questions this chapter answers

  1. Why are labels the exception?

58.1 What a scope is — not “where a name is visible” but “where it means that thing”#

Scope was introduced as “the region in which a name is visible”. More precisely, it is the region of the program in which a given identifier is established as denoting a particular thing. The same letters can denote something else in another region, or nothing at all.

So the question to ask is not “does this name exist?” but “what does this name mean here?”. That distinction runs through the whole chapter.

Scope is a property of the name, not of the object. An object can be perfectly alive where its name is not visible (chapter 45), and conversely a name can be visible while the object does not exist yet, or no longer does.

58.2 The four scopes#

The standard divides scope into four (C23 §6.2.1). Three are met constantly; the fourth causes misunderstandings if you have not met it.

scopefrom whereto wherewhat lives here
file scopethe point of declarationthe end of the translation uniteverything declared outside functions
block scopethe point of declarationthat block’s closing bracelocals, parameters, for’s first slot
function prototype scopethe parameter namethe prototype’s closing parenthesisparameter names in a prototype
function scopeanywhere in the functionthe whole functionlabels only

Table 58.1 — The four scopes the standard defines

examples-en/ch56/scopes.c

/* The four scopes a name can have --- exactly as the standard defines them. */
#include <stdio.h>

/* (1) file scope: visible from here to the end of this file */
static int file_level = 1;

/* (2) function prototype scope: the parameter names rows and cols vanish when
      this parenthesis closes. They are useful even so --- the size of a later
      parameter is written with them. */
void print_grid(int rows, int cols, int grid[rows][cols]);

/* (3) function scope: a label is visible throughout the function, even one written inside a block. */
static int find_first_negative(const int *a, int n)
{
    for (int i = 0; i < n; i++) {
        if (a[i] < 0) {
            goto found;          /* jump to a label inside the block below */
        }
    }
    return -1;
    {
    found:                       /* inside a block, yet visible anywhere in the function */
        return 0;
    }
}

void print_grid(int rows, int cols, int grid[rows][cols])
{
    for (int r = 0; r < rows; r++) {
        for (int c = 0; c < cols; c++) printf(" %d", grid[r][c]);
        putchar('\n');
    }
}

int main(void)
{
    /* (4) block scope: only inside these braces */
    int block_level = 2;
    {
        int inner = 3;
        printf("inner block sees: file=%d block=%d inner=%d\n",
               file_level, block_level, inner);
    }
    /* out here, inner is no longer a name */

    int grid[2][3] = {{1, 2, 3}, {4, 5, 6}};
    puts("\nthe grid, sized by names from the prototype scope:");
    print_grid(2, 3, grid);

    int values[] = {5, 7, -2, 9};
    printf("\nfirst negative found: %s\n",
           find_first_negative(values, 4) == 0 ? "yes" : "no");
    return 0;
}

Output

inner block sees: file=1 block=2 inner=3

the grid, sized by names from the prototype scope:
 1 2 3
 4 5 6

first negative found: yes

The demonstration shows all four at once. Two of them deserve a second look.

Function prototype scope is useful even though the name vanishes at once. A parameter name written in a prototype disappears when the parenthesis closes — which is why void f(int n); and void f(int); are exactly the same declaration. There are two reasons to write the name anyway: for the human reader, and to express the size of a later parameter with it.

void print_grid(int rows, int cols, int grid[rows][cols]);

rows and cols are used to spell the shape of the third parameter and then vanish. Without names this notation could not be written at all (chapter 40).

Only labels have function scope. The found: in the demonstration sits inside an inner block, yet is visible from anywhere in the function. That is what lets goto jump across block boundaries — and it is a privilege of labels alone. Variables never behave this way.

Q. Why are labels the exception?

A. Because a label has no value. The real reason a variable’s name is confined to its block is that the object it names comes and goes with the block; what a label names is a point in the code, which has nothing to come or go. So there is nothing to confine.

★ Still, “can jump” and “may jump” are different. Jumping into a block skips the declarations in it, and jumping into a block that contains a variable-length array is forbidden outright.

58.3 Where a scope begins — before the equals sign#

When a name is declared, its scope begins where the declarator ends — which is before the = of an initialiser. It sounds like a detail; the consequences are not.

examples-en/ch56/scope_start.c

/* A scope begins where the declarator ends --- before the equals sign, not after. */
#include <stdio.h>

typedef int meter;          /* a type name at file scope */

int main(void)
{
    /* i's scope has already begun *before* the = is reached, so sizeof(i)
       refers to the i just declared (and does not read its value). */
    int i = sizeof(i);
    printf("int i = sizeof(i);  -> %d\n", i);

    /* a pointer holding its own address works for the same reason */
    void *self = &self;
    printf("void *self = &self; -> self == &self is %s\n",
           self == (void *)&self ? "true" : "false");

    {
        /* Here meter is a type name. After the line below, meter is a
           *variable* in this block --- from where the declarator ended. */
        meter meter = 42;
        printf("meter meter = 42;   -> %d  (the type name is now shadowed)\n", meter);
    }

    /* outside the block, meter is a type name again */
    meter distance = 7;
    printf("outside the block, meter is a type again -> %d\n", distance);

    /* a name declared in for's first slot is gone when the loop ends */
    for (int n = 0; n < 3; n++) { /* n lives only here */ }
    /* out here n is not a name --- reusing it means nothing to the first one */
    for (int n = 10; n > 8; n--) { /* unrelated to the n above */ }
    puts("\ntwo loops used the name n; neither knows the other");
    return 0;
}

Output

int i = sizeof(i);  -> 4
void *self = &self; -> self == &self is true
meter meter = 42;   -> 42  (the type name is now shadowed)
outside the block, meter is a type again -> 7

two loops used the name n; neither knows the other

That is why int i = sizeof(i); gets through. By the time the right-hand side is reached, i already means “the int just declared”, and sizeof measures without reading, so nothing is amiss. void *self = &self; is sound for the same reason: taking an address needs no value.

Counter-example. The dark side of the same rule

int x = 10;
{
    int x = x + 1;    /* <- not the outer x. Itself. */
}

The inner x’s scope began before the =, so the x on the right is not the outer 10 but itself, with no value yet. The value is read, so this is undefined behaviour (chapter 54). Compilers usually say so under -Winit-self or -Wuninitialized, but with the warnings off it passes in silence.

The cure is not to memorise the rule but to pick a different name.

Type names follow the same rule. The meter meter = 42; in the demonstration is the proof: the first meter is still a type name, and from the end of the declarator onwards meter is a variable in that block.

58.4 Shadowing — the inner covers the outer#

Scopes may overlap. Declaring a name in an inner block that matches one outside makes the name mean the inner one inside that block. This is shadowing.

examples-en/ch56/shadow.c

/* Shadowing --- and the fact that the preprocessor knows nothing of scope. */
#include <stdio.h>

int count = 100;                 /* file scope */

static void layers(void)
{
    printf("file scope        : count = %d\n", count);
    int count = 10;              /* shadows the file-scope count */
    printf("function body     : count = %d\n", count);
    {
        int count = 1;           /* shadows it again */
        printf("inner block       : count = %d\n", count);
    }
    printf("back in the body  : count = %d\n", count);
    /* a shadowed name is not gone --- it is merely covered for a while */
}

static void define_inside(void)
{
    /* This directive has nothing to do with being "inside a function".
       Preprocessing finishes before compiling, and it knows no blocks. */
#define LIMIT 5
    printf("\ninside the function : LIMIT = %d\n", LIMIT);
}

static void far_away(void)
{
    /* A different function, and LIMIT is still alive --- its reach is not a
       scope but "from that point to the end of the file". */
    printf("another function    : LIMIT = %d\n", LIMIT);
}

int main(void)
{
    layers();
    define_inside();
    far_away();
    printf("\nthe file-scope count is still %d\n", count);
    return 0;
}

Output

file scope        : count = 100
function body     : count = 10
inner block       : count = 1
back in the body  : count = 10

inside the function : LIMIT = 5
another function    : LIMIT = 5

the file-scope count is still 100

A shadowed name does not disappear. It has merely yielded its spelling for a while, and it returns intact when the block ends — as the last line of the demonstration confirms.

The difficulty is that this is legal. C does not forbid shadowing, and for good reason: if it did, whether a local variable name compiled would depend on which global names happen to be in somebody else’s header. Forbidding it would break the encapsulation of names.

The discipline therefore lives in tools and habits rather than in the language.

meanswhat it buys
-Wshadowwarns at every declaration that covers an outer name
declaring narrowlydeclare at the point of use and there is less to cover
naminglong on the outside (config_limit), short on the inside (i, n)

Table 58.2 — Working discipline for shadowing

What -Wshadow says looks like this.

warning: declaration of 'count' shadows a global declaration [-Wshadow]

★ It is not on by default, and turning it on tends to make other people’s headers noisy. Enabling it for your own code only is the practical compromise.

58.5 The preprocessor knows nothing of scope#

The contrast that surprises people most often is this one. The #define LIMIT 5 in the demonstration is written inside a function, and yet it is not confined to that function: it is still alive in another one.

The reason is simple. The layer we will open in chapter 61 finishes before compilation, and it knows neither blocks nor functions. What a macro has is not a scope but a stretch — from the point of definition until #undef or the end of the file.

a C namea macro name
what bounds itblock structurefrom its definition to the end of the file
can an inner one cover ityesno — a clash simply replaces it
how it endsclose the block#undef

Table 58.3 — Two layers make names, by two different rules

★ So the convention of spelling macros in capitals is not a matter of taste but a safety device. These names get no protection from scope, so they are made conspicuous for humans to avoid.

58.6 Scope, lifetime, linkage — three that get confused#

Finally, the three side by side. They are especially easy to mix up because the single word static means different things in different places.

axisthe questionwhat settles it
scopewhat does this name mean hereblock structure (this chapter)
lifetimewhen does the object exist, from when to whenstorage duration (chapter 45)
linkageis it the same as that name in another unitstatic, extern (chapter 56)

Table 58.4 — Three questions about a name and its object

The three axes are independent, so all of these combinations exist.

A common misconception. If the name is not visible, the object is not there

No. A static variable inside a function loses its visible name the moment the function returns, yet the object stays and its value is still there on the next call. Conversely, hand out an address and the object can be touched from places its name never reaches — which is exactly what pointers are for.

★ The dangerous combination is therefore the place where the name is gone and so is the object: holding on to the address of a local after leaving its block is the classic case (chapter 45).

We can now say precisely where a name holds. The next chapter asks a different question: there are names that share a place and a spelling and still do not collide, because C has four separate yards for names to live in.