Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/MicrosoftDocs/cpp-docs/llms.txt

Use this file to discover all available pages before exploring further.

The function is the fundamental modular unit in C. A function is usually designed to perform a specific task, and its name often reflects that task. Every C program is composed of one or more functions, one of which must be named main. Functions contain declarations and statements that execute when the function is called. MSVC adds several extensions to the standard C function model, including multiple calling conventions, Microsoft-specific inline specifiers, DLL import/export attributes, and structured exception handling inside function bodies.

Function Declarations and Definitions

A function declaration (prototype) establishes the name, return type, and parameter types of a function that may be defined elsewhere. A function definition provides the actual body — the code that executes when the function is called. A definition also serves as a declaration.
/* Declaration (prototype) — placed in a header or before first use */
int add(int a, int b);

/* Definition — provides the body */
int add(int a, int b) {
    return a + b;
}

int main(void) {
    int result = add(3, 4);   /* call */
    printf("%d\n", result);   /* 7 */
    return 0;
}
The compiler uses the prototype to verify argument types and counts at each call site. If a prototype is available, MSVC performs type checking and performs implicit conversions where permitted. If no prototype is visible, the compiler emits a warning under C standards.
A function declaration must precede the first call to the function. This is why standard library header files (such as <stdio.h>) are included at the top of source files — they contain the prototypes for all library functions used in the program.

Function Prototypes

A function prototype includes parameter type information and enables the compiler to check arguments at call sites:
/* Prototype with parameter names (names are optional in prototypes) */
double hypotenuse(double a, double b);

/* Prototype without parameter names — equally valid */
double hypotenuse(double, double);

/* Function that explicitly takes no arguments */
int get_error_code(void);

/* Old-style declaration — accepts any arguments (avoid in new code) */
int legacy_func();

Return Types

A function can return any C type except arrays and functions. To return a void (no value), declare the return type as void:
void print_banner(const char *title) {
    printf("=== %s ===\n", title);
    /* no return statement required (or use bare return;) */
}

/* Return a struct by value */
typedef struct { int x; int y; } Point;

Point make_point(int x, int y) {
    Point p;
    p.x = x;
    p.y = y;
    return p;
}

Calling Conventions

A calling convention specifies how arguments are passed to a function, how the return value is communicated back, and which entity (caller or callee) is responsible for cleaning up the stack. MSVC supports several calling conventions, each with a specific use case.
__cdecl is the default calling convention for C programs. Arguments are pushed onto the stack in right-to-left order, and the caller cleans up the stack. This design supports variadic functions (like printf) because the caller knows how many arguments were pushed.
/* Explicit __cdecl — also the default for C */
int __cdecl sum(int a, int b) {
    return a + b;
}

/* Variadic function — requires __cdecl */
int __cdecl my_printf(const char *fmt, ...) {
    /* implementation */
    return 0;
}
On x86, __cdecl is the default. On x64, there is only one native calling convention and __cdecl is silently ignored.

Inline Functions

The inline keyword asks the compiler to substitute the function body directly at every call site, eliminating the call overhead. MSVC also supports the Microsoft-specific __inline (synonym for inline) and __forceinline (stronger hint that the compiler should inline regardless of heuristics).
/* Standard C99 inline */
inline int square(int x) {
    return x * x;
}

/* Microsoft synonym — available even in C89 mode */
__inline int cube(int x) {
    return x * x * x;
}

/* __forceinline — compiler relaxes "too large to inline" heuristic */
__forceinline int clamp(int val, int lo, int hi) {
    if (val < lo) return lo;
    if (val > hi) return hi;
    return val;
}
Inline substitution occurs only at the compiler’s discretion. If a function’s address is taken, or if the function is too large, the compiler will not inline it even with __forceinline. For compatibility, _inline and _forceinline (single underscore) are also available as synonyms.
Inline functions generate faster and sometimes smaller code than the equivalent function call because:
  • They save the time required to prepare the stack for arguments and a return value.
  • They eliminate the jump-and-return overhead.
  • The compiler can optimize inline code together with the surrounding code (cross-procedure optimization).

Storage Class for Functions

Functions can have extern or static storage class. If no storage-class specifier appears in a function definition, the default is extern (visible across all translation units in the program).
/* extern is the default — visible program-wide */
extern int public_api(int x);

/* static — visible only within this .c file */
static int internal_helper(int x) {
    return x * 2;
}

int public_api(int x) {
    return internal_helper(x) + 1;
}
A function with static storage class is visible only in the source file in which it is defined. Use static to implement private helper functions that should not pollute the global namespace.

Variadic Functions

Variadic functions accept a variable number of arguments after the last declared parameter. The standard macros in <stdarg.h> allow you to traverse the extra arguments at run time.
#include <stdarg.h>
#include <stdio.h>

/* At least one fixed parameter must precede the ellipsis */
int sum_all(int count, ...) {
    va_list args;
    int total = 0;

    va_start(args, count);          /* initialize to first variable argument */

    for (int i = 0; i < count; i++) {
        total += va_arg(args, int); /* retrieve next argument as int */
    }

    va_end(args);                   /* clean up the va_list */
    return total;
}

int main(void) {
    printf("%d\n", sum_all(4, 10, 20, 30, 40)); /* 100 */
    return 0;
}
The four variadic macros work as follows:
MacroPurpose
va_listType that holds state for iterating variable arguments
va_start(ap, last)Initializes ap to point just after the last named parameter last
va_arg(ap, type)Returns the next argument as the given type and advances ap
va_end(ap)Performs cleanup; must be called before the function returns
The caller must pass the correct number and types of arguments. There is no runtime mechanism to detect mismatches. Passing the wrong type to va_arg leads to undefined behavior. Always document the expected calling protocol clearly.

Building a Custom printf-style Function

A common pattern is to wrap vprintf (which accepts a va_list) when building logging utilities:
#include <stdarg.h>
#include <stdio.h>
#include <time.h>

void log_message(const char *level, const char *fmt, ...) {
    va_list args;
    time_t  now = time(NULL);
    char    timestamp[32];

    strftime(timestamp, sizeof(timestamp), "%H:%M:%S", localtime(&now));
    fprintf(stderr, "[%s] [%s] ", timestamp, level);

    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);

    fputc('\n', stderr);
}

int main(void) {
    log_message("INFO",  "Server started on port %d", 8080);
    log_message("ERROR", "Cannot open file: %s", "config.ini");
    return 0;
}

Function Pointers

A function pointer stores the address of a function and can be used to call that function indirectly. Function pointers are the basis for callbacks, dispatch tables, and polymorphism in C.
#include <stdlib.h>
#include <stdio.h>

/* Declare a function pointer type with typedef */
typedef int (*CompareFunc)(const void *, const void *);

/* Comparison function compatible with qsort */
int compare_ints(const void *a, const void *b) {
    return (*(const int *)a) - (*(const int *)b);
}

int main(void) {
    int data[] = { 5, 2, 8, 1, 9, 3 };
    int n = sizeof(data) / sizeof(data[0]);

    /* Pass function pointer to qsort */
    qsort(data, n, sizeof(int), compare_ints);

    for (int i = 0; i < n; i++) {
        printf("%d ", data[i]);  /* 1 2 3 5 8 9 */
    }
    printf("\n");
    return 0;
}

Dispatch Table (Jump Table)

Function pointers are commonly stored in arrays to form dispatch tables:
typedef void (*HandlerFunc)(int);

void on_open(int fd)  { printf("Open  fd=%d\n", fd); }
void on_close(int fd) { printf("Close fd=%d\n", fd); }
void on_read(int fd)  { printf("Read  fd=%d\n", fd); }

/* Indexed dispatch table */
HandlerFunc dispatch[] = {
    on_open,    /* event 0 */
    on_close,   /* event 1 */
    on_read     /* event 2 */
};

void handle_event(int event, int fd) {
    if (event >= 0 && event < 3) {
        dispatch[event](fd);  /* indirect call */
    }
}

Calling Convention in Function Pointer Types

When using calling conventions other than the default, the convention must appear in the function pointer type:
/* Pointer to a __stdcall function */
typedef int (__stdcall *WinApiFunc)(void *, const char *, const char *, unsigned);

WinApiFunc fn_ptr = MessageBoxA;  /* Windows API example */

DLL Import and Export

MSVC uses __declspec(dllexport) and __declspec(dllimport) to mark functions for export from or import into a DLL. The __declspec attribute is typically wrapped in a macro for portability:
#ifdef BUILDING_MY_DLL
#  define MY_API __declspec(dllexport)
#else
#  define MY_API __declspec(dllimport)
#endif

MY_API int  my_function(int x);
MY_API void my_other_function(const char *msg);

See Also

Build docs developers (and LLMs) love