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.
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.000000Calling 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 -
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
