C Mastery / Declarations, Definitions, and Identifiers
Part 1 — The Core Language

Declarations, Definitions, and Identifiers

This chapter distinguishes declarations from definitions, explains how identifiers are formed, and shows how the two halves of the C name system fit together. This is the foundation for scope, linkage, and multi-file programs.

Why This Matters

The declaration/definition split is the reason C can compile multiple files separately and link them later. Confusing the two leads directly to duplicate definitions, missing definitions, and "undefined reference" linker errors.

Prerequisites

Core Concept

Identifiers

An identifier is a name you give to an object, function, type, label, struct member, or macro. It is a sequence of letters, digits, and underscores, not beginning with a digit. a, count, _private, and MAX_SIZE are all identifiers.

C is case-sensitive: count, Count, and COUNT are three different names.

Reserved identifiers you must not use (in a hosted program, unless the standard grants them to you):

underscore (_Foo, __x) are reserved for the implementation.

implementation (even if followed by a lowercase letter).

header is included.

Declarations

A declaration introduces a name and associates it with a type. It tells the compiler "there is a thing with this name and this type" without necessarily reserving storage or producing code.

extern int counter;   /* declaration only: no storage reserved */
int max(int a, int b); /* function declaration (prototype) */

Definitions

A definition is a declaration that *also* reserves storage (for an object) or provides the body (for a function).

int counter = 0;         /* definition: reserves storage */
int max(int a, int b) {  /* definition: provides the body */
    return a > b ? a : b;
}

Every definition is also a declaration, but not every declaration is a definition.

The one-definition rule (in practice)

Across an entire program, an identifier with external linkage may have exactly one definition (the "one definition rule," though C states it as a constraint that there be no more than one external definition). It may have many declarations, but they must all be compatible.

Syntax

Object declaration forms

int x;               /* tentative definition (file scope) */
extern int y;        /* declaration only */
int z = 5;           /* definition with initializer */

At file scope, int x; without extern and without an initializer is a tentative definition: if no other definition appears in the translation unit, it becomes a definition initialized to 0. If a definition with an initializer appears later, the tentative definition is just a declaration.

Function declaration forms

int f(int a, int b);      /* prototype: names optional but useful */
int f(int, int);          /* prototype: names omitted */
int f();                  /* old-style: unspecified arguments (avoid) */

The prototype form int f(int, int); tells the compiler the parameter types, enabling type checking and argument conversions.

Examples

Declaration vs. definition

/* header.h */
extern int shared_count;       /* declaration */
int compute(int x);            /* declaration */

/* impl.c */
int shared_count = 0;          /* definition */
int compute(int x) {           /* definition */
    return x * 2;
}

Every translation unit that includes header.h sees the declaration. Exactly one translation unit (impl.c) provides the definition.

Tentative definition

int value;   /* tentative definition */

int main(void)
{
    return value;   /* returns 0 (static storage is zero-initialized) */
}

Because value is never given an explicit initializer, the tentative definition becomes a definition initialized to 0.

How It Works

Declarations give the compiler enough type information to generate correct references to a name. Definitions give the compiler (or the linker, for external symbols) the actual storage or code. The linker's job is to match every external reference (from a declaration) to its single definition.

Variations

Typedefs are declarations, not definitions

typedef unsigned long size_t;

A typedef introduces a name for a type but reserves no storage and produces no code. It is a declaration of an alias.

Struct declarations vs. definitions

struct Node;              /* declaration of an incomplete type */
struct Node { int v; };   /* definition (completes the type) */

A struct *type* can be declared incomplete and defined later. This enables opaque types and forward references (c.struct.decl).

Common Mistakes

definition linker error. Use extern declarations in headers and define in one .c file (unless inline/static changes linkage, c.func.inline).

linker error.

(tentative definition).

Undefined Behavior

units is a constraint violation and, if undiagnosed, leads to UB.

behavior depending on context.

Portability

implementations reserve additional names (e.g., __attribute__ on GCC).

obsolescent; avoid them.

Under the Hood

The compiler records declarations in a symbol table for the translation unit. External declarations produce entries the linker must resolve; definitions produce definitions in the object file's symbol table. Linkage (c.lang.linkage) determines which symbols are visible across translation units.

Practical Usage

shared mutable globals.

Exercises

1. Write a header declaring a function and a source file defining it, plus a main that calls it. Build and run. 2. Deliberately create a duplicate-definition error by defining a global in a header included twice, and observe the linker diagnostic. 3. Explain what a tentative definition is and demonstrate it with a program that reads an uninitialized file-scope int.

Deep Challenge

Explain why the following produces a linker error, and identify the exact diagnostic category:

/* a.c */
int helper(void) { return 1; }

/* b.c */
extern int helper(void);
int main(void) { return helper(); }

Then add a second definition of helper in a third file and explain what the standard says about multiple external definitions.

how long objects live.

References

§6.9 (external definitions), §7.1.3 (reserved identifiers).

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.lang.ident04
c.lang.decl05
c.lang.def05