Fundamentals 17 min read

Master C Functions and Macros: From Basics to Advanced Preprocessor Tricks

This article provides a comprehensive guide to C functions—including their categories, signatures, parameter passing, pointer parameters, variadic arguments, callbacks, and recursion—and delves into the C preprocessor, covering directives, macro definitions, operators, and practical macro examples for safer and more expressive code.

AI Cyberspace
AI Cyberspace
AI Cyberspace
Master C Functions and Macros: From Basics to Advanced Preprocessor Tricks

Functions in C

C functions can be divided into three categories: the main function main() as the program entry, built‑in functions provided by the C standard library, and user‑defined functions.

A function consists of the following parts:

Return type

Function name (identifier)

Parameter list

Function body

Function signature (function name plus parameter list)

Parameters vs. Arguments

There are two ways to pass values to parameters:

Value passing : the argument value is copied to the parameter, creating independent data.

Reference passing : the argument’s address (pointer) is passed, so the parameter refers to the same data and modifications affect the original.

Pointer‑type Parameters

#include <stdio.h>
#include <time.h>

void getSeconds(unsigned long *par);

int main()
{
    unsigned long sec;
    getSeconds(&sec);
    printf("Number of seconds: %ld
", sec);
    return 0;
}

void getSeconds(unsigned long *par)
{
    *par = time(NULL);
    return;
}

Variadic Parameter Lists

Variadic functions accept an unspecified number of arguments. The stdarg.h header provides macros to handle them.

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

double Avg(int num, ...)
{
    va_list valist;
    double sum = 0.0;
    int i;
    va_start(valist, num);
    for (i = 0; i < num; i++) {
        sum += va_arg(valist, int);
    }
    va_end(valist);
    return sum / num;
}

int main()
{
    printf("Average of 2, 3, 4, 5 = %f
", Avg(4, 2, 3, 4, 5));
    printf("Average of 5, 10, 15 = %f
", Avg(3, 5, 10, 15));
    return 0;
}

Running the program produces:

$ ./main
Average of 2, 3, 4, 5 = 3.500000
Average of 5, 10, 15 = 10.000000

Calling and Returning

When a function is called, required arguments are passed; if the function returns a value, it can be stored.

#include <stdio.h>

int max(int num1, int num2);

int main()
{
    int a = 100;
    int b = 200;
    int ret = max(a, b);
    printf("Max value is : %d
", ret);
    return 0;
}

int max(int num1, int num2)
{
    int result;
    if (num1 > num2)
        result = num1;
    else
        result = num2;
    return result;
}

Pointer Functions and Function Pointers

A pointer function returns a pointer. Example that generates ten random numbers and returns a pointer to a static array:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int* getRandom()
{
    static int r[10];
    int i;
    srand((unsigned)time(NULL));
    for (i = 0; i < 10; ++i) {
        r[i] = rand();
        printf("%d
", r[i]);
    }
    return r;
}

int main()
{
    int *p;
    int i;
    p = getRandom();
    for (i = 0; i < 10; i++) {
        printf("*(p + [%d]) : %d
", i, *(p + i));
    }
    return 0;
}

A function pointer is a pointer to a function with a specific signature, effectively an alias for the function.

#include <stdio.h>

int max(int x, int y) { return x > y ? x : y; }

int main(void)
{
    int (*p)(int, int) = &max;
    int a, b, c, d;
    printf("Input three numbers:");
    scanf("%d %d %d", &a, &b, &c);
    d = p(p(a, b), c);
    printf("MAX: %d
", d);
    return 0;
}

Callback Functions

A callback is a function invoked by another function when a specific event occurs, typically registered via a function pointer.

#include <stdlib.h>
#include <stdio.h>

void populate_array(int *array, size_t arraySize, int (*getNextValue)(void))
{
    size_t i;
    for (i = 0; i < arraySize; i++) {
        array[i] = getNextValue();
    }
}

int getNextRandomValue(void) { return rand(); }

int main()
{
    int myarray[10];
    populate_array(myarray, 10, &getNextRandomValue);
    for (int i = 0; i < 10; i++) {
        printf("%d ", myarray[i]);
    }
    printf("
");
    return 0;
}

Recursive Functions

Recursion occurs when a function calls itself. Proper exit conditions are essential to avoid infinite loops. Recursion is useful for problems like factorial calculation and Fibonacci sequence generation.

C Preprocessor

The C Preprocessor (CPP) is a separate step before compilation that performs textual substitution. It is often used to detect the target operating system and generate platform‑specific code.

Preprocessor Directives

Directives start with ‘#’ and usually appear at the top of a source file.

Macros

A macro is a textual replacement defined by a preprocessor directive. It consists of a macro name and a macro body, which the preprocessor substitutes before compilation.

#define <macro_name> <macro_body>

Guidelines for using macros:

Place macro definitions in header files.

Use uppercase with underscores for macro names.

Keep macro definitions simple and clear.

Prefer block comments over line comments inside macros.

Use functions instead of complex macros when possible.

Prefer typedef over macros for custom types.

Prefer const over macros for constants.

Macro Definition Directives

#define : defines a macro, e.g., #define MAX_ARRAY_LENGTH 20 #undef : undefines a macro, e.g.,

#undef FILE_SIZE
#define FILE_SIZE 42

#ifdef : checks if a macro is defined.

#ifndef : checks if a macro is not defined.

Macro Functions

Parameterized macros can simulate functions. Example:

#define square(x) ((x) * (x))

Macro Operators

Line‑Continuation Operator (\)

Use \ to continue a macro definition onto the next line.

#define message_for(a, b)  \
    printf(#a " and " #b ": We love you!
")

Token‑Pasting Operator (##)

Concatenates two tokens.

#define tokenpaster(n) printf("token" #n " = %d", token##n)

Stringification Operator (#)

Converts a macro argument into a string literal. <code>#define message_for(a, b) \ printf(#a " and " #b ": We love you!\n") </code> Variadic Macro Operator (__VA_ARGS__) Allows a macro to accept a variable number of arguments. <code>#define PR(...) printf(__VA_ARGS__) </code> Complex Macro Definitions Using Loops Complex macros can be wrapped in do { ... } while (0) to ensure correct behavior within control structures. <code>#define foo(x) do{ \ bar(x); \ baz(x); \ } while (0) </code> Built‑in Macros C provides several predefined macros such as __FILE__ , __DATE__ , __TIME__ , __LINE__ , and __STDC__ . <code>#include <stdio.h> int main() { printf("File :%s\n", __FILE__); printf("Date :%s\n", __DATE__); printf("Time :%s\n", __TIME__); printf("Line :%d\n", __LINE__); printf("ANSI :%d\n", __STDC__); return 0; } </code> Common Macros Header guard: #ifndef COMDEF_H #define COMDEF_H ... #endif Memory access: #define MEM_B(x) (*((byte*)(x))) Maximum/minimum: #define MAX(x,y) (((x)>(y)) ? (x) : (y)) Array size: #define ARR_SIZE(a) (sizeof(a)/sizeof((a)[0])) - END -

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

CfunctionspointersvariadicMacrosPreprocessor
AI Cyberspace
Written by

AI Cyberspace

AI, big data, cloud computing, and networking.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.