README
Variable Arguments (Variadic Functions) in C
Table of Contents
- •Introduction
- •The stdarg.h Header
- •How Variadic Functions Work
- •Basic Variadic Function Structure
- •va_list, va_start, va_arg, va_end
- •va_copy Macro
- •Determining Argument Count
- •Type Safety Considerations
- •Common Patterns
- •Implementing printf-like Functions
- •Variadic Macros
- •Best Practices
- •Common Pitfalls
- •Real-World Examples
- •Summary
Introduction
Variable argument functions (also called variadic functions) are functions that accept a variable number of arguments. This is a powerful feature in C that allows creating flexible functions like printf(), scanf(), and many utility functions.
Why Use Variadic Functions?
- •Flexibility: Accept any number of arguments
- •Convenience: Single function for multiple use cases
- •Code Reuse: Avoid writing multiple overloaded functions
- •Standard Library: Many C functions use this feature
Common Examples in Standard Library
/* printf - variable number of arguments for formatting */
printf("Hello %s, you are %d years old\n", name, age);
/* scanf - variable number of arguments for input */
scanf("%d %f", &integer, &floating);
/* fprintf - similar to printf with file stream */
fprintf(stderr, "Error: %s (code %d)\n", message, code);
/* sprintf - format into string */
sprintf(buffer, "Result: %d", value);
The stdarg.h Header
The <stdarg.h> header provides macros for working with variable arguments.
Components of stdarg.h
| Component | Type/Description |
|---|---|
va_list | Type for holding information about variable arguments |
va_start | Macro to initialize va_list |
va_arg | Macro to retrieve the next argument |
va_end | Macro to clean up va_list |
va_copy | Macro to copy va_list (C99) |
Basic Include
#include <stdarg.h>
/* Now you can use:
* - va_list
* - va_start
* - va_arg
* - va_end
* - va_copy (C99)
*/
How Variadic Functions Work
Function Declaration Syntax
/* General syntax */
return_type function_name(fixed_params, ...);
/* Examples */
int sum(int count, ...); /* Count-based */
void print_strings(const char *first, ...); /* Sentinel-based */
int printf(const char *format, ...); /* Format-based */
The Ellipsis (...)
- •The
...indicates variable arguments - •Must appear at the end of the parameter list
- •Requires at least one fixed (named) parameter before it
Memory Layout
When a variadic function is called, arguments are typically pushed onto the stack:
Higher addresses
+------------------+
| Last vararg |
+------------------+
| ... |
+------------------+
| First vararg |
+------------------+
| Last fixed arg | <- va_list starts after this
+------------------+
| ... |
+------------------+
| First fixed arg |
+------------------+
| Return address |
+------------------+
Lower addresses
Basic Variadic Function Structure
Template
#include <stdarg.h>
return_type function_name(fixed_param, ...) {
va_list args; /* 1. Declare va_list */
va_start(args, fixed_param); /* 2. Initialize with last fixed param */
/* 3. Process arguments using va_arg */
type value = va_arg(args, type);
va_end(args); /* 4. Clean up */
return result;
}
Simple Example: Sum of Integers
#include <stdio.h>
#include <stdarg.h>
/* Sum a variable number of integers */
int sum(int count, ...) {
va_list args;
int total = 0;
va_start(args, count);
for (int i = 0; i < count; i++) {
total += va_arg(args, int);
}
va_end(args);
return total;
}
int main(void) {
printf("Sum: %d\n", sum(3, 10, 20, 30)); /* 60 */
printf("Sum: %d\n", sum(5, 1, 2, 3, 4, 5)); /* 15 */
return 0;
}
va_list, va_start, va_arg, va_end
va_list
A type that holds information needed to retrieve additional arguments.
va_list args; /* Declare a variable of type va_list */
va_start
Initializes the va_list to point to the first variable argument.
/* Syntax */
void va_start(va_list ap, last_fixed_param);
/* Example */
void my_func(int x, const char *fmt, ...) {
va_list args;
va_start(args, fmt); /* Initialize after 'fmt' */
/* ... */
va_end(args);
}
Important: The second parameter must be the name of the last fixed parameter.
va_arg
Retrieves the next argument of the specified type.
/* Syntax */
type va_arg(va_list ap, type);
/* Examples */
int i = va_arg(args, int);
double d = va_arg(args, double);
char *s = va_arg(args, char *);
Type Promotion Rules:
- •
charandshortare promoted toint - •
floatis promoted todouble
/* WRONG - char is promoted to int */
char c = va_arg(args, char);
/* CORRECT */
char c = (char)va_arg(args, int);
/* WRONG - float is promoted to double */
float f = va_arg(args, float);
/* CORRECT */
float f = (float)va_arg(args, double);
va_end
Cleans up the va_list. Must be called before the function returns.
/* Syntax */
void va_end(va_list ap);
/* Example */
void my_func(int count, ...) {
va_list args;
va_start(args, count);
/* Process arguments */
va_end(args); /* Clean up - REQUIRED */
}
va_copy Macro
The va_copy macro (C99) creates a copy of a va_list. This is useful when you need to process arguments multiple times.
Syntax
void va_copy(va_list dest, va_list src);
Usage Example
#include <stdio.h>
#include <stdarg.h>
void process_twice(int count, ...) {
va_list args1, args2;
va_start(args1, count);
va_copy(args2, args1); /* Create a copy */
/* First pass: calculate sum */
int sum = 0;
for (int i = 0; i < count; i++) {
sum += va_arg(args1, int);
}
printf("Sum: %d\n", sum);
/* Second pass: print each value */
printf("Values: ");
for (int i = 0; i < count; i++) {
printf("%d ", va_arg(args2, int));
}
printf("\n");
va_end(args1);
va_end(args2); /* Must end both! */
}
int main(void) {
process_twice(4, 10, 20, 30, 40);
return 0;
}
Determining Argument Count
Since variadic functions don't automatically know how many arguments were passed, you need a way to determine the count.
Method 1: Explicit Count Parameter
int sum(int count, ...) {
va_list args;
int total = 0;
va_start(args, count);
for (int i = 0; i < count; i++) {
total += va_arg(args, int);
}
va_end(args);
return total;
}
/* Usage */
sum(3, 10, 20, 30);
Method 2: Sentinel Value
Use a special value to mark the end of arguments.
int sum_until_zero(..., 0) {
va_list args;
int total = 0;
int val;
va_start(args, /* first arg */);
while ((val = va_arg(args, int)) != 0) {
total += val;
}
va_end(args);
return total;
}
/* Better: First argument is not variadic */
int sum_until_zero(int first, ...) {
va_list args;
int total = first;
int val;
if (first == 0) return 0;
va_start(args, first);
while ((val = va_arg(args, int)) != 0) {
total += val;
}
va_end(args);
return total;
}
/* Usage */
sum_until_zero(10, 20, 30, 0); /* 0 is sentinel */
Method 3: Format String (like printf)
void my_printf(const char *format, ...) {
va_list args;
va_start(args, format);
for (const char *p = format; *p; p++) {
if (*p == '%' && *(p+1)) {
p++;
switch (*p) {
case 'd':
printf("%d", va_arg(args, int));
break;
case 's':
printf("%s", va_arg(args, char*));
break;
case 'f':
printf("%f", va_arg(args, double));
break;
case '%':
putchar('%');
break;
}
} else {
putchar(*p);
}
}
va_end(args);
}
Method 4: NULL-Terminated Pointer List
void print_strings(const char *first, ...) {
va_list args;
const char *str;
va_start(args, first);
/* Print first string */
printf("%s", first);
/* Print remaining strings until NULL */
while ((str = va_arg(args, const char*)) != NULL) {
printf(" %s", str);
}
printf("\n");
va_end(args);
}
/* Usage - NULL terminates the list */
print_strings("Hello", "World", "!", NULL);
Type Safety Considerations
The Problem
Variadic functions have no type checking for variable arguments:
int sum(int count, ...) {
va_list args;
va_start(args, count);
int total = 0;
for (int i = 0; i < count; i++) {
total += va_arg(args, int); /* Assumes int! */
}
va_end(args);
return total;
}
/* This compiles but causes undefined behavior! */
sum(3, 10, 20.5, "hello"); /* Wrong types */
Solutions
1. Document Expected Types
/**
* Calculate sum of integers
* @param count Number of integers following
* @param ... Integers to sum (must be int type)
*/
int sum(int count, ...);
2. Use Type-Indicating Parameters
typedef enum { T_INT, T_DOUBLE, T_STRING, T_END } ArgType;
void print_values(ArgType type, ...) {
va_list args;
va_start(args, type);
while (type != T_END) {
switch (type) {
case T_INT:
printf("%d ", va_arg(args, int));
break;
case T_DOUBLE:
printf("%f ", va_arg(args, double));
break;
case T_STRING:
printf("%s ", va_arg(args, char*));
break;
default:
break;
}
type = va_arg(args, ArgType);
}
va_end(args);
printf("\n");
}
/* Usage */
print_values(T_INT, 42, T_DOUBLE, 3.14, T_STRING, "hello", T_END);
3. GCC format Attribute
/* Tell compiler this is printf-like */
__attribute__((format(printf, 1, 2)))
void my_printf(const char *fmt, ...);
/* Compiler will now warn about type mismatches */
Common Patterns
Pattern 1: Sum/Average of Numbers
double average(int count, ...) {
va_list args;
double sum = 0.0;
va_start(args, count);
for (int i = 0; i < count; i++) {
sum += va_arg(args, double);
}
va_end(args);
return count > 0 ? sum / count : 0.0;
}
Pattern 2: Maximum/Minimum
int max(int count, ...) {
va_list args;
va_start(args, count);
int max_val = va_arg(args, int); /* First value */
for (int i = 1; i < count; i++) {
int val = va_arg(args, int);
if (val > max_val) {
max_val = val;
}
}
va_end(args);
return max_val;
}
Pattern 3: String Concatenation
char *concat(const char *first, ...) {
va_list args;
size_t total_len = 0;
/* First pass: calculate total length */
va_start(args, first);
for (const char *s = first; s != NULL; s = va_arg(args, const char*)) {
total_len += strlen(s);
}
va_end(args);
/* Allocate result buffer */
char *result = malloc(total_len + 1);
if (!result) return NULL;
result[0] = '\0';
/* Second pass: concatenate strings */
va_start(args, first);
for (const char *s = first; s != NULL; s = va_arg(args, const char*)) {
strcat(result, s);
}
va_end(args);
return result;
}
/* Usage */
char *str = concat("Hello", " ", "World", "!", NULL);
free(str);
Pattern 4: Array Construction
int *make_array(size_t *out_size, ...) {
va_list args, args_copy;
va_start(args, out_size);
va_copy(args_copy, args);
/* Count elements (until -1 sentinel) */
size_t count = 0;
while (va_arg(args, int) != -1) {
count++;
}
va_end(args);
/* Allocate and fill array */
int *arr = malloc(count * sizeof(int));
if (!arr) {
va_end(args_copy);
return NULL;
}
for (size_t i = 0; i < count; i++) {
arr[i] = va_arg(args_copy, int);
}
va_end(args_copy);
*out_size = count;
return arr;
}
/* Usage */
size_t size;
int *arr = make_array(&size, 10, 20, 30, 40, -1);
Implementing printf-like Functions
Basic Custom Printf
#include <stdio.h>
#include <stdarg.h>
void my_printf(const char *format, ...) {
va_list args;
va_start(args, format);
for (const char *p = format; *p != '\0'; p++) {
if (*p != '%') {
putchar(*p);
continue;
}
/* Handle format specifier */
p++; /* Skip '%' */
switch (*p) {
case 'd':
case 'i':
printf("%d", va_arg(args, int));
break;
case 'u':
printf("%u", va_arg(args, unsigned int));
break;
case 'f':
printf("%f", va_arg(args, double));
break;
case 's': {
char *s = va_arg(args, char*);
printf("%s", s ? s : "(null)");
break;
}
case 'c':
putchar(va_arg(args, int)); /* char promoted to int */
break;
case 'p':
printf("%p", va_arg(args, void*));
break;
case 'x':
printf("%x", va_arg(args, unsigned int));
break;
case 'X':
printf("%X", va_arg(args, unsigned int));
break;
case '%':
putchar('%');
break;
default:
putchar('%');
putchar(*p);
break;
}
}
va_end(args);
}
Using vprintf, vfprintf, vsprintf
C provides v versions of printf functions that accept va_list:
#include <stdio.h>
#include <stdarg.h>
/* Custom error logging function */
void error_log(const char *format, ...) {
va_list args;
va_start(args, format);
fprintf(stderr, "[ERROR] ");
vfprintf(stderr, format, args); /* Use vfprintf with va_list */
fprintf(stderr, "\n");
va_end(args);
}
/* Custom debug logging with line info */
void debug_log(const char *file, int line, const char *format, ...) {
va_list args;
va_start(args, format);
printf("[DEBUG] %s:%d: ", file, line);
vprintf(format, args);
printf("\n");
va_end(args);
}
#define DEBUG(...) debug_log(__FILE__, __LINE__, __VA_ARGS__)
/* Safe sprintf wrapper */
int safe_sprintf(char *buffer, size_t size, const char *format, ...) {
va_list args;
va_start(args, format);
int result = vsnprintf(buffer, size, format, args);
va_end(args);
return result;
}
v-Functions Reference
| printf-style | v-version | Description |
|---|---|---|
printf | vprintf | Print to stdout |
fprintf | vfprintf | Print to file stream |
sprintf | vsprintf | Print to string |
snprintf | vsnprintf | Print to string (bounded) |
scanf | vscanf | Read from stdin |
fscanf | vfscanf | Read from file stream |
sscanf | vsscanf | Read from string |
Variadic Macros
C99 introduced variadic macros using __VA_ARGS__.
Basic Variadic Macro
#define DEBUG_PRINT(fmt, ...) \
printf("[DEBUG] " fmt "\n", __VA_ARGS__)
/* Usage */
DEBUG_PRINT("Value: %d", 42);
DEBUG_PRINT("Name: %s, Age: %d", "John", 30);
Problem: Empty VA_ARGS
/* This doesn't work with zero variable arguments */
DEBUG_PRINT("Hello"); /* Error: trailing comma */
Solution 1: GNU Extension (##VA_ARGS)
#define DEBUG_PRINT(fmt, ...) \
printf("[DEBUG] " fmt "\n", ##__VA_ARGS__)
/* The ## removes the trailing comma if __VA_ARGS__ is empty */
DEBUG_PRINT("Hello"); /* OK! */
Solution 2: C23 VA_OPT
/* C23 standard way */
#define DEBUG_PRINT(fmt, ...) \
printf("[DEBUG] " fmt __VA_OPT__(,) __VA_ARGS__ "\n")
Practical Variadic Macro Examples
/* Logging with file and line */
#define LOG(level, fmt, ...) \
fprintf(stderr, "[%s] %s:%d: " fmt "\n", \
level, __FILE__, __LINE__, ##__VA_ARGS__)
#define LOG_INFO(fmt, ...) LOG("INFO", fmt, ##__VA_ARGS__)
#define LOG_WARN(fmt, ...) LOG("WARN", fmt, ##__VA_ARGS__)
#define LOG_ERROR(fmt, ...) LOG("ERROR", fmt, ##__VA_ARGS__)
/* Conditional debugging */
#ifdef DEBUG
#define DBG(fmt, ...) printf("[DBG] " fmt "\n", ##__VA_ARGS__)
#else
#define DBG(fmt, ...) ((void)0)
#endif
/* Assert with message */
#define ASSERT_MSG(cond, fmt, ...) \
do { \
if (!(cond)) { \
fprintf(stderr, "Assertion failed: %s\n", #cond); \
fprintf(stderr, fmt "\n", ##__VA_ARGS__); \
abort(); \
} \
} while(0)
Best Practices
1. Always Call va_end
void func(int count, ...) {
va_list args;
va_start(args, count);
/* Process... */
va_end(args); /* ALWAYS call this! */
}
2. Use the Correct Types
/* Remember type promotion! */
int get_char(int dummy, ...) {
va_list args;
va_start(args, dummy);
/* WRONG: char is promoted to int */
/* char c = va_arg(args, char); */
/* CORRECT */
char c = (char)va_arg(args, int);
va_end(args);
return c;
}
3. Validate Argument Count
int safe_sum(int count, ...) {
if (count <= 0) {
return 0;
}
va_list args;
va_start(args, count);
int sum = 0;
for (int i = 0; i < count; i++) {
sum += va_arg(args, int);
}
va_end(args);
return sum;
}
4. Use GCC Attributes for Type Checking
/* Enable printf-style type checking */
__attribute__((format(printf, 2, 3)))
void log_message(int level, const char *fmt, ...);
/* Now compiler warns about mismatched types */
log_message(1, "Count: %d", "string"); /* Warning! */
5. Document Expected Arguments
/**
* Calculate the sum of integers.
*
* @param count The number of integer arguments that follow
* @param ... Variable number of integers to sum
* @return The sum of all provided integers
*
* @warning All variable arguments must be of type int
*
* Example:
* int result = sum(3, 10, 20, 30); // returns 60
*/
int sum(int count, ...);
Common Pitfalls
Pitfall 1: Wrong Type in va_arg
void wrong_usage(int count, ...) {
va_list args;
va_start(args, count);
/* If caller passes int, but we read double: UNDEFINED! */
double val = va_arg(args, double); /* DANGER */
va_end(args);
}
wrong_usage(1, 42); /* Passes int, reads as double - WRONG! */
Pitfall 2: Reading Too Many Arguments
void dangerous(int count, ...) {
va_list args;
va_start(args, count);
for (int i = 0; i < count + 5; i++) { /* Reading past end! */
printf("%d ", va_arg(args, int)); /* UNDEFINED */
}
va_end(args);
}
Pitfall 3: Forgetting Type Promotion
void process_chars(int count, ...) {
va_list args;
va_start(args, count);
/* WRONG - char is promoted to int */
for (int i = 0; i < count; i++) {
char c = va_arg(args, char); /* WRONG! */
printf("%c", c);
}
va_end(args);
}
/* CORRECT version */
void process_chars_correct(int count, ...) {
va_list args;
va_start(args, count);
for (int i = 0; i < count; i++) {
char c = (char)va_arg(args, int); /* CORRECT */
printf("%c", c);
}
va_end(args);
}
Pitfall 4: Not Calling va_end
void leaky_function(int count, ...) {
va_list args;
va_start(args, count);
if (count <= 0) {
return; /* WRONG: va_end not called! */
}
/* Process... */
va_end(args);
}
/* CORRECT version */
void fixed_function(int count, ...) {
va_list args;
va_start(args, count);
if (count <= 0) {
va_end(args); /* Clean up before return */
return;
}
/* Process... */
va_end(args);
}
Pitfall 5: Reusing va_list Without Reset
void wrong_reuse(int count, ...) {
va_list args;
va_start(args, count);
/* First pass */
for (int i = 0; i < count; i++) {
printf("%d ", va_arg(args, int));
}
/* Second pass - WRONG! args is exhausted */
for (int i = 0; i < count; i++) {
printf("%d ", va_arg(args, int)); /* UNDEFINED! */
}
va_end(args);
}
/* CORRECT: Use va_copy */
void correct_reuse(int count, ...) {
va_list args, args_copy;
va_start(args, count);
va_copy(args_copy, args);
/* First pass */
for (int i = 0; i < count; i++) {
printf("%d ", va_arg(args, int));
}
/* Second pass - using the copy */
for (int i = 0; i < count; i++) {
printf("%d ", va_arg(args_copy, int));
}
va_end(args);
va_end(args_copy);
}
Real-World Examples
1. Logging System
#include <stdio.h>
#include <stdarg.h>
#include <time.h>
typedef enum {
LOG_DEBUG,
LOG_INFO,
LOG_WARNING,
LOG_ERROR
} LogLevel;
static LogLevel min_level = LOG_DEBUG;
static FILE *log_file = NULL;
void log_init(LogLevel level, const char *filename) {
min_level = level;
if (filename) {
log_file = fopen(filename, "a");
}
}
void log_cleanup(void) {
if (log_file) {
fclose(log_file);
log_file = NULL;
}
}
void log_message(LogLevel level, const char *fmt, ...) {
if (level < min_level) return;
FILE *out = log_file ? log_file : stderr;
/* Get timestamp */
time_t now = time(NULL);
struct tm *tm_info = localtime(&now);
char time_buf[20];
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", tm_info);
/* Level names */
const char *level_names[] = {"DEBUG", "INFO", "WARN", "ERROR"};
/* Print header */
fprintf(out, "[%s] [%s] ", time_buf, level_names[level]);
/* Print message */
va_list args;
va_start(args, fmt);
vfprintf(out, fmt, args);
va_end(args);
fprintf(out, "\n");
fflush(out);
}
/* Convenience macros */
#define LOG_DBG(...) log_message(LOG_DEBUG, __VA_ARGS__)
#define LOG_INF(...) log_message(LOG_INFO, __VA_ARGS__)
#define LOG_WRN(...) log_message(LOG_WARNING, __VA_ARGS__)
#define LOG_ERR(...) log_message(LOG_ERROR, __VA_ARGS__)
2. Error Handler with Context
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
int code;
char message[256];
char file[64];
int line;
} Error;
Error *error_create(int code, const char *file, int line,
const char *fmt, ...) {
Error *err = malloc(sizeof(Error));
if (!err) return NULL;
err->code = code;
strncpy(err->file, file, sizeof(err->file) - 1);
err->line = line;
va_list args;
va_start(args, fmt);
vsnprintf(err->message, sizeof(err->message), fmt, args);
va_end(args);
return err;
}
void error_print(const Error *err) {
if (!err) return;
fprintf(stderr, "Error %d at %s:%d: %s\n",
err->code, err->file, err->line, err->message);
}
#define ERROR(code, fmt, ...) \
error_create(code, __FILE__, __LINE__, fmt, ##__VA_ARGS__)
3. Dynamic SQL Query Builder
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
char *build_insert_query(const char *table, int col_count, ...) {
va_list args;
char buffer[1024];
char values[512];
int offset = 0;
int val_offset = 0;
offset = snprintf(buffer, sizeof(buffer), "INSERT INTO %s (", table);
val_offset = snprintf(values, sizeof(values), ") VALUES (");
va_start(args, col_count);
for (int i = 0; i < col_count; i++) {
const char *col = va_arg(args, const char*);
const char *val = va_arg(args, const char*);
if (i > 0) {
offset += snprintf(buffer + offset, sizeof(buffer) - offset, ", ");
val_offset += snprintf(values + val_offset, sizeof(values) - val_offset, ", ");
}
offset += snprintf(buffer + offset, sizeof(buffer) - offset, "%s", col);
val_offset += snprintf(values + val_offset, sizeof(values) - val_offset, "'%s'", val);
}
va_end(args);
snprintf(values + val_offset, sizeof(values) - val_offset, ")");
strncat(buffer, values, sizeof(buffer) - strlen(buffer) - 1);
return strdup(buffer);
}
/* Usage */
/*
char *query = build_insert_query("users", 3,
"name", "John",
"email", "john@example.com",
"age", "30");
// Result: INSERT INTO users (name, email, age) VALUES ('John', 'john@example.com', '30')
free(query);
*/
4. Menu Builder
#include <stdio.h>
#include <stdarg.h>
typedef void (*MenuAction)(void);
typedef struct {
const char *label;
MenuAction action;
} MenuItem;
void run_menu(const char *title, int item_count, ...) {
va_list args;
MenuItem items[20];
va_start(args, item_count);
for (int i = 0; i < item_count && i < 20; i++) {
items[i].label = va_arg(args, const char*);
items[i].action = va_arg(args, MenuAction);
}
va_end(args);
while (1) {
printf("\n=== %s ===\n", title);
for (int i = 0; i < item_count; i++) {
printf("%d. %s\n", i + 1, items[i].label);
}
printf("0. Exit\n");
printf("Choice: ");
int choice;
if (scanf("%d", &choice) != 1) {
while (getchar() != '\n');
continue;
}
if (choice == 0) break;
if (choice > 0 && choice <= item_count) {
items[choice - 1].action();
}
}
}
Summary
Key Points
- •
Header: Include
<stdarg.h>for variadic function support - •
Macros:
- •
va_list- Type to hold argument information - •
va_start(args, last_fixed)- Initialize the list - •
va_arg(args, type)- Get next argument - •
va_end(args)- Clean up (always call!) - •
va_copy(dest, src)- Copy for reuse (C99)
- •
- •
Type Promotion:
- •
charandshort→int - •
float→double
- •
- •
Counting Methods:
- •Explicit count parameter
- •Sentinel value (NULL, 0, -1)
- •Format string parsing
- •
Best Practices:
- •Always call
va_end() - •Use correct types (account for promotion)
- •Validate argument count
- •Document expected argument types
- •Use compiler attributes for type checking
- •Always call
Quick Reference
#include <stdarg.h>
int variadic_func(int count, ...) {
va_list args;
va_start(args, count);
int result = 0;
for (int i = 0; i < count; i++) {
result += va_arg(args, int);
}
va_end(args);
return result;
}
Common v-Functions
/* For printf-like wrapper functions */
vprintf(format, args);
vfprintf(file, format, args);
vsnprintf(buffer, size, format, args);
References
- •ISO/IEC 9899 - C Standard
- •GNU C Library Manual - Variadic Functions
- •C: A Reference Manual by Harbison and Steele
- •Expert C Programming by Peter van der Linden
- •The C Programming Language by Kernighan and Ritchie