Placement Prep

Function Overloading in C: Why It Fails and What Works

C does not support function overloading natively. Learn why, and explore C11 _Generic, stdarg.h, and function pointers as the standard workarounds.

By FACE Prep Team 4 min read
c-programming function-overloading c11 generic-selection placement-prep technical-interview c-vs-cpp

C does not support function overloading: one function name maps to one definition, and the C standard has no mechanism to change that.

That’s the answer interviewers want when they ask “does C support function overloading?” in placement technical rounds. Students who studied C++ first often assume the answer is yes. It isn’t, and the reason is architectural.

Why C Has No Function Overloading

C++ supports overloading because the C++ compiler uses name-mangling: it encodes each function’s parameter types into the binary symbol name. A function add(int, int) becomes something like _Z3addii in the object file; add(float, float) becomes _Z3addff. The linker sees two distinct symbols and resolves each independently.

C deliberately omits name-mangling. The C compiler writes the function name directly into the object file, unchanged. Two functions named add produce two definitions for the same symbol, and the linker returns an error. This is a design choice, not a bug. C prioritises stable, predictable symbol names for compatibility with assemblers, linkers, and foreign-language interfaces. Calling C from Python via ctypes, from Rust via FFI, or from assembly code all depend on knowing that a function named add emits the symbol add, nothing more.

The result: if you need type-based dispatch in C, you implement it yourself. Four standard techniques cover the majority of real use cases.

_Generic: Compile-Time Type Selection (C11)

The C11 standard introduced the _Generic selection expression. Combined with a macro, it gives callers a single call-site syntax that dispatches to the correct type-specific function at compile time.

#include <stdio.h>

void addInt(int a, int b)       { printf("int sum: %d\n",   a + b); }
void addFloat(float a, float b) { printf("float sum: %.2f\n", a + b); }

#define add(x, y) _Generic((x), \
    int:   addInt,              \
    float: addFloat             \
)(x, y)

int main(void) {
    add(3, 4);        /* dispatches to addInt   -> int sum: 7   */
    add(3.5f, 4.5f);  /* dispatches to addFloat -> float sum: 8.00 */
    return 0;
}

_Generic evaluates the type of its controlling expression at compile time and selects the matching branch. No runtime overhead. No manual type tag. The call site writes add(3, 4) exactly as it would in C++. Requires C11 or later: compile with gcc -std=c11 or clang -std=c11.

This is the closest approximation to native C++ overloading available in standard C. For new code targeting C11+, start here.

Variable Arguments with stdarg.h

When the number of arguments varies across call sites (not just their types), <stdarg.h> provides va_list, va_start, va_arg, and va_end.

#include <stdio.h>
#include <stdarg.h>

void printAll(int count, ...) {
    va_list args;
    va_start(args, count);
    for (int i = 0; i < count; i++) {
        printf("%d ", va_arg(args, int));
    }
    va_end(args);
    printf("\n");
}

int main(void) {
    printAll(3, 10, 20, 30);    /* prints: 10 20 30 */
    printAll(5, 1, 2, 3, 4, 5); /* prints: 1 2 3 4 5 */
    return 0;
}

The catch with va_arg

va_arg requires you to know the type of each argument at the call site. There is no compile-time type checking. Passing a float but reading it as int produces undefined behaviour, silently. Use <stdarg.h> when argument count varies and you fully control the calling code. It is not a safe substitute for type-checked overloading.

Function Pointers and void*

Two older techniques work on C89 and C99 targets, where _Generic is not available.

Function pointers

#include <stdio.h>

void addInt(int a, int b)       { printf("int: %d\n",   a + b); }
void addFloat(float a, float b) { printf("float: %.2f\n", a + b); }

int main(void) {
    void (*fnInt)(int, int)       = addInt;
    void (*fnFloat)(float, float) = addFloat;

    fnInt(3, 5);         /* int: 8    */
    fnFloat(3.5f, 2.5f); /* float: 6.00 */
    return 0;
}

The caller selects the function explicitly. No magic, no runtime overhead, available since C89. Works well when the dispatch decision is made once and then the pointer is passed around.

void* with a type tag

#include <stdio.h>

void add(void *a, void *b, char type) {
    if (type == 'i')
        printf("int: %d\n",   *(int *)a + *(int *)b);
    else if (type == 'f')
        printf("float: %.2f\n", *(float *)a + *(float *)b);
}

int main(void) {
    int   x = 5, y = 10;
    float p = 2.5f, q = 3.5f;

    add(&x, &y, 'i'); /* int: 15   */
    add(&p, &q, 'f'); /* float: 6.00 */
    return 0;
}

A single function accepts any pointer type; the type tag controls dispatch. The entire type-safety burden shifts to the programmer. A wrong tag produces incorrect output or a crash, with no compiler warning. Use void* only when _Generic is unavailable and the codebase already depends on generic pointer APIs.

C vs. C++: What the Compiler Does Differently

FeatureC (workarounds)C++ (native)
Function overloadingNot supportedSupported via name-mangling
Type safetyManual (programmer’s burden)Compiler-enforced
Default parametersNot supportedSupported
_Generic dispatchC11+ onlySupported (rarely needed)
Variadic functions<stdarg.h><cstdarg> or variadic templates
Runtime dispatch costZero (function pointers aside)Zero (compile-time resolved)

The simplest rule: if overloading is the feature you need and you have a choice of language, use C++. If you are in a C codebase (embedded firmware, Linux kernel modules, FFI surfaces), _Generic handles compile-time dispatch and function pointers handle runtime dispatch.

Which Approach Fits Which Use Case

A quick decision guide:

  • C11 available, fixed argument count, types vary: use _Generic.
  • Argument count varies, types are uniform: use <stdarg.h> via va_list.
  • C89/C99 target, runtime selection required: use function pointers.
  • Generic pointer API, older codebase: use void* with a type tag, and document the expected types carefully.
  • Language choice available: switch to C++, where overloading is native and compiler-enforced.

Knowing this taxonomy signals to a placement interviewer that you understand C’s design choices, not just its syntax. For more practice on the conceptual side, the must-solve conceptual C programming questions cover pointer arithmetic, storage classes, and type conversion rules that often appear in the same screening round. The full set of C technical interview questions tracked by FACE Prep shows how frequently this topic pairs with pointer-to-function and typedef questions. And if you are working through output-prediction and pattern problems, the C programs asked in placement interviews rounds out the preparation.

The same principle behind _Generic, selecting the right implementation based on a type at compile time, shows up again in LLM function-calling APIs, where you register typed function signatures and the model dispatches to the correct one. TinkerLLM is the hands-on starting point for that layer, at ₹299.

Primary sources

Frequently asked questions

Does C support function overloading?

No. The C compiler maps each function name to exactly one definition in the object file. There is no built-in mechanism to distinguish two functions with the same name but different parameter types.

What is _Generic in C11?

_Generic is a compile-time selection expression introduced in the C11 standard. It selects a branch expression based on the type of a controlling expression, allowing macros to dispatch to type-specific functions at compile time.

Why does C++ support function overloading but C does not?

C++ uses name-mangling: the compiler encodes parameter types into the function's binary symbol name, letting the linker resolve multiple definitions of the same name. C deliberately omits name-mangling to keep symbol names stable for linkers and foreign-language interfaces.

Can macros simulate function overloading in C?

Yes, using a _Generic-based macro in C11 or later. The macro resolves to the correct type-specific function at compile time, giving the caller a single call-site syntax similar to C++ overloading.

What is name-mangling and why does C lack it?

Name-mangling is a compiler technique that encodes a function's parameter types into its symbol name. C omits it to maintain predictable, stable symbol names needed by assemblers, linkers, and foreign-language interfaces like Python ctypes or assembly call sites.

Will interviewers ask about function overloading in C placement rounds?

Yes, regularly. The expected answer is that C lacks native overloading and that _Generic (C11) is the standard alternative. Confusing C and C++ behaviour on this point is one of the most common mistakes in C technical rounds.

Build AI projects

A self-paced playground for building with LLMs.

TinkerLLM is FACE Prep's sister property. A guided environment for shipping real LLM applications, the kind of project that earns a paragraph on your resume, not a line.

Try TinkerLLM (₹299 launch)
Free AI Roadmap PDF