C / Editorial solution guide

Resolve an Undefined Reference During Linking

Identify the missing definition and repair the actual link inputs instead of changing a correct declaration.

About 3 min readContent reviewed

Read-only knowledge base · No public comments

Recognize the symptoms

Compilation of an individual source succeeds, but the final build reports a message such as "undefined reference to `counter_next'". The exact quoting and wording vary with the linker. No new executable is produced, or an old executable remains at the expected path.

Understand the likely causes

  • A header supplied the declaration while the source containing the definition was omitted. Other possibilities include a spelling mismatch, a file-scope static definition that is intentionally private, an absent library, or incompatible build configuration. A header inclusion alone never supplies a separate object file’s executable definition.

1. Confirm the failing build phase

Read the first relevant diagnostic and the full command. A syntax or incompatible-type error belongs to compilation; an unresolved external symbol belongs to linking. Record the exact missing name and the translation unit that references it. Work in a disposable lab directory containing copies of the minimal sources, and keep production build outputs untouched.

2. Find the authoritative definition

Search source text for the function definition, not merely its prototype or calls. Compare spelling and parameter types with the public header. If the only definition is static in another .c file, decide whether it should remain private; publish a deliberate external interface rather than removing static indiscriminately. Include the public header in the defining source to expose inconsistencies.

3. Supply the definition to the link

For a component split into main.c and counter.c, compile and link both sources with the compiler driver. The illustrative command assumes those lab files already exist and use compatible declarations. If the definition comes from a library, follow that library’s official include and link instructions; on order-sensitive linkers, library placement can matter. Do not include counter.c text directly to hide a missing build dependency.

bash Link all required lab translation units
cc -std=c17 -Wall -Wextra -Wpedantic main.c counter.c -o lab_counter

This shell command requires the named source files and a GCC-compatible C driver.

4. Compare with a complete working program

This single-file control example contains both the declaration and a matching definition. It separates a language-interface problem from a missing-object problem. It is intentionally simple: the helper requires current below INT_MAX and a writable output pointer, conditions satisfied by main. Splitting the declaration into a header and the definition into counter.c should preserve its behavior when both sources are linked.

c A definition available to its caller
#include <stdio.h>

void counter_next(int current, int *out);

void counter_next(int current, int *out)
{
    *out = current + 1;
}

int main(void)
{
    int next = 0;
    counter_next(8, &next);
    printf("next=%d\n", next);
    return 0;
}
Expected output
next=9

The documented fixed inputs satisfy the helper contract and produce nine.

5. Check configuration after source repair

If the definition exists and is linked, check target architecture, language driver, library variant, and conditional compilation. C and C++ use different language and linkage conventions; changing a C file to C++ is not a general repair. Rebuild affected objects from current sources in the isolated directory so stale artifacts cannot mask a mismatch.

Verify the fix

Require a zero build exit status, confirm that the intended executable was freshly produced, and run only the synthetic control input. The complete example must print next=9 followed by a newline. For the split build, inspect the recorded source list and confirm that both units participate. A surviving old executable is not evidence that the new link succeeded.

Keep it from recurring

  • Keep declarations in one guarded header, include it in callers and definitions, and track header dependencies. Record source and library inputs in the build configuration. Distinguish private static helpers from exported functions, and keep compiler options consistent across translation units.

Official references