liveBook · Manning

Public visibility

As explained in Modern C, Third Edition

Visibility is the property determining whether an identifier can be referenced from a particular part of a program.

An identifier is visible where its declaration can be used. In C, that region is its scope: visibility begins at the declaration, not necessarily at the beginning of the surrounding block. A local declaration lasts through its enclosing block; a variable declared by a for construct is confined to that construct. Parameters are visible in their function body, while a function declared at file scope is visible from its declaration to the end of the file. TAKEAWAY: determine visibility from the declaration and its scope, not from a general notion that a name is “public.” section 2.2 section 13.2

Nested braces can change which declaration a name denotes. A declaration of x in an inner block shadows an outer x until that block ends. This does not make the outer object unusable: a pointer saved before the shadowing declaration can still refer to it. section 13.2

Listing 13.1 An example of shadowing with local variables
#include "c23-fallback.h"

void squareIt(double* p) [[__unsequenced__]] {
  *p *= *p;
}
int main(void) {  1 
  double x = 35.0; 2 
  double* xp = &x; 
  {                                3                   
    squareIt(&x);               /* Refers to double x */ 
    4 
    [[maybe_unused]] int x = 0; /* Shadow double x */ 
    5 
    squareIt(xp);               /* Valid use of double x */ 
    6 
  }
  7 
  squareIt(&x);                 /* Refers to double x */ 

}

Follow x through the inner block: the inner declaration changes which x can be named, while xp continues to point to the outer double object. section 13.2

Visibility also differs from object identity. In the extern example, a local definition creates a distinct object, but an extern declaration inside the if block refers to the existing file-scope object. The two declarations visible at different points need not designate different objects. section 13.2

Listing 13.2 An example of shadowing with an extern variable
#include <stdio.h> 1 

unsigned i = 1;        

int main(void) { 2 
  unsigned i = 2;        /* A new object */
  if (i) {            3 
    extern unsigned i;   /* An existing object */ 
    printf("%u\n", i);  /* prints 1 */
  } else {
    printf("%u\n", i);  /* prints 2 */
  }
}

Compare the local unsigned i with the extern unsigned i inside the if block; the latter makes printf print the file-scope value, 1. section 13.2

A struct nested inside another struct does not acquire a private struct-declaration scope. The two struct declarations have the same scope: if declared at file scope, both are subsequently visible in the file; if declared inside a function, both are bound to the enclosing compound statement. TAKEAWAY: nesting a struct type in C does not by itself restrict that type’s visibility to the outer struct. section 6.3.3

How Modern C, Third Edition covers it

How deep it goes
Works through declaration boundaries, shadowing, nested structs, and extern declarations; distinguishes name visibility from access to an object.
Examples
Uses nested local variables, a saved pointer, an extern declaration, and a struct declared inside another struct.
What it assumes
Assumes familiarity with C functions, blocks, declarations, structs, and pointers; defines scope and shadowing where needed.
What you can do after
Diagnose/debug which declaration a C name refers to and whether an object remains accessible when its name is shadowed.
How it connects
Connects visibility to declarations, block scope, shadowing, pointers, struct declarations, and storage duration.

Also explained in

Compare how 8 books explain public visibility →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Modern C, Third Edition. The section links point to the parts of the book it draws on.