c
exercises
exercises.c🔧c
/**
* Introduction to the C Preprocessor - Exercises
*
* Complete the following exercises to practice using
* preprocessor directives, macros, and conditional compilation.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* ============================================================
* Exercise 1: Basic Constant Macros
*
* Define the following constant macros:
* - DAYS_IN_WEEK: Number of days in a week
* - HOURS_IN_DAY: Number of hours in a day
* - MINUTES_IN_HOUR: Number of minutes in an hour
* - SECONDS_IN_MINUTE: Number of seconds in a minute
*
* Then define SECONDS_IN_WEEK using the other macros.
* ============================================================ */
/* TODO: Define the macros here */
void test_exercise1(void) {
printf("=== Exercise 1: Basic Constant Macros ===\n");
#ifdef SECONDS_IN_WEEK
printf("Days in week: %d\n", DAYS_IN_WEEK);
printf("Hours in day: %d\n", HOURS_IN_DAY);
printf("Minutes in hour: %d\n", MINUTES_IN_HOUR);
printf("Seconds in minute: %d\n", SECONDS_IN_MINUTE);
printf("Seconds in week: %d\n", SECONDS_IN_WEEK);
printf("Expected: 604800\n");
#else
printf("Macros not defined yet!\n");
#endif
printf("\n");
}
/* ============================================================
* Exercise 2: Function-like Macros
*
* Define the following function-like macros with proper
* parentheses to avoid operator precedence issues:
*
* - AVERAGE(a, b): Calculate average of two numbers
* - SWAP(a, b): Swap two integers using XOR (no temp needed)
* - IS_ODD(n): Check if number is odd (returns 1 or 0)
* - IS_EVEN(n): Check if number is even (returns 1 or 0)
* - CELSIUS_TO_FAHRENHEIT(c): Convert Celsius to Fahrenheit
* ============================================================ */
/* TODO: Define the macros here */
void test_exercise2(void) {
printf("=== Exercise 2: Function-like Macros ===\n");
#ifdef AVERAGE
printf("AVERAGE(10, 20) = %.2f (expected: 15.00)\n", (double)AVERAGE(10, 20));
printf("IS_ODD(7) = %d (expected: 1)\n", IS_ODD(7));
printf("IS_ODD(8) = %d (expected: 0)\n", IS_ODD(8));
printf("IS_EVEN(8) = %d (expected: 1)\n", IS_EVEN(8));
printf("CELSIUS_TO_FAHRENHEIT(100) = %.1f (expected: 212.0)\n",
(double)CELSIUS_TO_FAHRENHEIT(100));
printf("CELSIUS_TO_FAHRENHEIT(0) = %.1f (expected: 32.0)\n",
(double)CELSIUS_TO_FAHRENHEIT(0));
int x = 5, y = 10;
printf("\nBefore SWAP: x=%d, y=%d\n", x, y);
/* SWAP(x, y); */
/* printf("After SWAP: x=%d, y=%d\n", x, y); */
#else
printf("Macros not defined yet!\n");
#endif
printf("\n");
}
/* ============================================================
* Exercise 3: Debug Macros with Stringification
*
* Create the following debug macros:
*
* - DEBUG_INT(var): Print variable name and integer value
* Example: DEBUG_INT(count) prints "count = 42"
*
* - DEBUG_FLOAT(var): Print variable name and float value
* Example: DEBUG_FLOAT(pi) prints "pi = 3.140000"
*
* - DEBUG_STR(var): Print variable name and string value
* Example: DEBUG_STR(name) prints "name = "John""
*
* - DEBUG_EXPR(expr): Print expression and its integer result
* Example: DEBUG_EXPR(a + b) prints "a + b = 15"
* ============================================================ */
/* TODO: Define the macros here using # operator */
void test_exercise3(void) {
printf("=== Exercise 3: Debug Macros ===\n");
#ifdef DEBUG_INT
int count = 42;
float pi = 3.14f;
const char *name = "John";
int a = 10, b = 5;
DEBUG_INT(count);
DEBUG_FLOAT(pi);
DEBUG_STR(name);
DEBUG_EXPR(a + b);
DEBUG_EXPR(a * b);
DEBUG_EXPR(count / 2);
#else
printf("Macros not defined yet!\n");
#endif
printf("\n");
}
/* ============================================================
* Exercise 4: Token Pasting for Getter/Setter
*
* Create macros to generate getter and setter functions:
*
* DECLARE_PROPERTY(type, name):
* - Declares a static variable: static type name;
* - Declares getter: type get_name(void);
* - Declares setter: void set_name(type value);
*
* DEFINE_PROPERTY(type, name):
* - Defines the getter and setter functions
*
* Example:
* DECLARE_PROPERTY(int, score)
* expands to:
* static int score;
* int get_score(void);
* void set_score(int value);
* ============================================================ */
/* TODO: Define the macros here using ## operator */
void test_exercise4(void) {
printf("=== Exercise 4: Token Pasting for Properties ===\n");
#ifdef DECLARE_PROPERTY
/* The property definitions would go here */
printf("Token pasting example ready\n");
/*
* Expected usage:
* set_score(100);
* printf("Score: %d\n", get_score());
*/
#else
printf("Macros not defined yet!\n");
#endif
printf("\n");
}
/* ============================================================
* Exercise 5: Conditional Compilation
*
* Create conditional code that:
* 1. If DEBUG_LEVEL is 0: Only ERROR messages compile
* 2. If DEBUG_LEVEL is 1: ERROR and WARN messages compile
* 3. If DEBUG_LEVEL is 2: ERROR, WARN, and INFO compile
* 4. If DEBUG_LEVEL is 3: All messages compile (including DEBUG)
*
* Define macros LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG
* that either print or do nothing based on DEBUG_LEVEL.
* ============================================================ */
#define DEBUG_LEVEL 2 /* Change this to test different levels */
/* TODO: Define the LOG macros here */
void test_exercise5(void) {
printf("=== Exercise 5: Conditional Compilation ===\n");
printf("DEBUG_LEVEL = %d\n\n", DEBUG_LEVEL);
#ifdef LOG_ERROR
LOG_ERROR("This is an error message");
LOG_WARN("This is a warning message");
LOG_INFO("This is an info message");
LOG_DEBUG("This is a debug message");
#else
printf("Macros not defined yet!\n");
#endif
printf("\n");
}
/* ============================================================
* Exercise 6: Platform-Specific Macros
*
* Create macros that define:
* - PLATFORM_NAME: String with platform name
* - PATH_SEPARATOR: '/' for Unix, '\\' for Windows
* - LINE_ENDING: "\n" for Unix, "\r\n" for Windows
* - MAX_PATH_LENGTH: 4096 for Unix, 260 for Windows
*
* Use #if defined() to detect the platform.
* ============================================================ */
/* TODO: Define platform-specific macros here */
void test_exercise6(void) {
printf("=== Exercise 6: Platform-Specific Macros ===\n");
#ifdef PLATFORM_NAME
printf("Platform: %s\n", PLATFORM_NAME);
printf("Path separator: '%c'\n", PATH_SEPARATOR);
printf("Max path length: %d\n", MAX_PATH_LENGTH);
#else
printf("Macros not defined yet!\n");
#endif
printf("\n");
}
/* ============================================================
* Exercise 7: Safe Memory Macros
*
* Create macros for safe memory operations:
*
* - SAFE_MALLOC(ptr, type, count):
* Allocates memory for 'count' elements of 'type',
* assigns to ptr, prints error and exits if fails.
*
* - SAFE_REALLOC(ptr, type, count):
* Reallocates ptr for 'count' elements of 'type',
* prints error and exits if fails.
*
* - SAFE_FREE(ptr):
* Frees ptr and sets it to NULL.
*
* Use do-while(0) pattern for multi-statement macros.
* ============================================================ */
/* TODO: Define the macros here */
void test_exercise7(void) {
printf("=== Exercise 7: Safe Memory Macros ===\n");
#ifdef SAFE_MALLOC
int *arr = NULL;
SAFE_MALLOC(arr, int, 10);
if (arr) {
for (int i = 0; i < 10; i++) arr[i] = i * 10;
printf("Array values: ");
for (int i = 0; i < 10; i++) printf("%d ", arr[i]);
printf("\n");
SAFE_FREE(arr);
printf("After SAFE_FREE: arr = %s\n", arr == NULL ? "NULL" : "not null");
}
#else
printf("Macros not defined yet!\n");
#endif
printf("\n");
}
/* ============================================================
* Exercise 8: Enum and String Array Generation
*
* Create a macro system that generates both an enum and
* a corresponding string array from a single definition.
*
* DEFINE_ENUM(name, entries) should:
* - Create an enum with the given entries
* - Create a const char* array with string versions
*
* Example:
* DEFINE_ENUM(Color, RED, GREEN, BLUE)
*
* Creates:
* enum Color { RED, GREEN, BLUE, COLOR_COUNT };
* const char *Color_strings[] = { "RED", "GREEN", "BLUE" };
* ============================================================ */
/* TODO: Define the macro here */
/* Hint: Use X-macros pattern */
void test_exercise8(void) {
printf("=== Exercise 8: Enum and String Array ===\n");
/* Example of X-macro pattern (if you implement it):
*
* #define COLOR_LIST(X) \
* X(RED) \
* X(GREEN) \
* X(BLUE)
*
* enum Color { COLOR_LIST(ENUM_ENTRY) };
* const char *color_names[] = { COLOR_LIST(STRING_ENTRY) };
*/
printf("Implement X-macro pattern to generate enum and strings\n");
printf("\n");
}
/* ============================================================
* Exercise 9: Compile-Time Assertions
*
* Create a STATIC_ASSERT macro that causes a compile-time
* error if a condition is false.
*
* Example: STATIC_ASSERT(sizeof(int) == 4, "int must be 4 bytes")
*
* Hint: Use a negative array size to trigger error.
* Common pattern: typedef char assertion_name[(condition) ? 1 : -1];
* ============================================================ */
/* TODO: Define STATIC_ASSERT macro here */
void test_exercise9(void) {
printf("=== Exercise 9: Compile-Time Assertions ===\n");
#ifdef STATIC_ASSERT
/* These should compile successfully */
STATIC_ASSERT(sizeof(char) == 1, "char_size");
STATIC_ASSERT(sizeof(short) >= 2, "short_size");
STATIC_ASSERT(sizeof(int) >= 2, "int_size");
/* This would fail to compile if sizeof(int) != 4 */
/* STATIC_ASSERT(sizeof(int) == 4, "int_must_be_4_bytes"); */
printf("Static assertions passed!\n");
printf("sizeof(char) = %zu\n", sizeof(char));
printf("sizeof(short) = %zu\n", sizeof(short));
printf("sizeof(int) = %zu\n", sizeof(int));
#else
printf("STATIC_ASSERT not defined yet!\n");
#endif
printf("\n");
}
/* ============================================================
* Exercise 10: Complete Logging System
*
* Create a complete logging system with these macros:
*
* Configuration:
* - ENABLE_LOGGING: Master switch to enable/disable all logging
* - LOG_SHOW_FILE: Show filename in log output
* - LOG_SHOW_LINE: Show line number in log output
* - LOG_SHOW_FUNC: Show function name in log output
*
* Macros:
* - LOG(level, fmt, ...): Main logging macro
* - LOG_ENTER(): Log "Entering function_name"
* - LOG_LEAVE(): Log "Leaving function_name"
* - LOG_VAR(var): Log variable name and value
*
* The LOG macro should support printf-style formatting.
* ============================================================ */
#define ENABLE_LOGGING
#define LOG_SHOW_FILE
#define LOG_SHOW_LINE
/* #define LOG_SHOW_FUNC */
/* TODO: Define the logging macros here */
void test_logging_helper(void) {
#ifdef LOG
/* LOG_ENTER(); */
int value = 42;
/* LOG_VAR(value); */
/* LOG("INFO", "Processing value: %d", value); */
/* LOG_LEAVE(); */
(void)value;
#endif
}
void test_exercise10(void) {
printf("=== Exercise 10: Complete Logging System ===\n");
#ifdef LOG
LOG("INFO", "Starting test");
LOG("DEBUG", "Value is %d", 42);
LOG("WARN", "Something might be wrong");
LOG("ERROR", "Operation failed with code %d", -1);
test_logging_helper();
#else
printf("Logging macros not defined yet!\n");
#endif
printf("\n");
}
/* ============================================================
* Main Function - Run All Tests
* ============================================================ */
int main(void) {
printf("\n");
printf("╔══════════════════════════════════════════════════════════╗\n");
printf("║ Introduction to Preprocessor - Exercises ║\n");
printf("╚══════════════════════════════════════════════════════════╝\n\n");
test_exercise1();
test_exercise2();
test_exercise3();
test_exercise4();
test_exercise5();
test_exercise6();
test_exercise7();
test_exercise8();
test_exercise9();
test_exercise10();
printf("╔══════════════════════════════════════════════════════════╗\n");
printf("║ Complete the TODOs above! ║\n");
printf("╚══════════════════════════════════════════════════════════╝\n\n");
return 0;
}
/* ============================================================
* ANSWER KEY
* ============================================================
*
* Exercise 1: Basic Constant Macros
* ---------------------------------
* #define DAYS_IN_WEEK 7
* #define HOURS_IN_DAY 24
* #define MINUTES_IN_HOUR 60
* #define SECONDS_IN_MINUTE 60
* #define SECONDS_IN_WEEK (DAYS_IN_WEEK * HOURS_IN_DAY * \
* MINUTES_IN_HOUR * SECONDS_IN_MINUTE)
*
*
* Exercise 2: Function-like Macros
* --------------------------------
* #define AVERAGE(a, b) (((a) + (b)) / 2.0)
* #define SWAP(a, b) do { (a) ^= (b); (b) ^= (a); (a) ^= (b); } while(0)
* #define IS_ODD(n) (((n) & 1) != 0)
* #define IS_EVEN(n) (((n) & 1) == 0)
* #define CELSIUS_TO_FAHRENHEIT(c) (((c) * 9.0 / 5.0) + 32.0)
*
*
* Exercise 3: Debug Macros
* ------------------------
* #define DEBUG_INT(var) printf(#var " = %d\n", (var))
* #define DEBUG_FLOAT(var) printf(#var " = %f\n", (var))
* #define DEBUG_STR(var) printf(#var " = \"%s\"\n", (var))
* #define DEBUG_EXPR(expr) printf(#expr " = %d\n", (expr))
*
*
* Exercise 4: Token Pasting
* -------------------------
* #define DECLARE_PROPERTY(type, name) \
* static type name; \
* type get_##name(void); \
* void set_##name(type value)
*
* #define DEFINE_PROPERTY(type, name) \
* type get_##name(void) { return name; } \
* void set_##name(type value) { name = value; }
*
*
* Exercise 5: Conditional Compilation
* -----------------------------------
* #if DEBUG_LEVEL >= 3
* #define LOG_DEBUG(msg) printf("[DEBUG] %s\n", msg)
* #else
* #define LOG_DEBUG(msg)
* #endif
*
* #if DEBUG_LEVEL >= 2
* #define LOG_INFO(msg) printf("[INFO] %s\n", msg)
* #else
* #define LOG_INFO(msg)
* #endif
*
* #if DEBUG_LEVEL >= 1
* #define LOG_WARN(msg) printf("[WARN] %s\n", msg)
* #else
* #define LOG_WARN(msg)
* #endif
*
* #if DEBUG_LEVEL >= 0
* #define LOG_ERROR(msg) printf("[ERROR] %s\n", msg)
* #else
* #define LOG_ERROR(msg)
* #endif
*
*
* Exercise 6: Platform-Specific Macros
* ------------------------------------
* #if defined(_WIN32) || defined(_WIN64)
* #define PLATFORM_NAME "Windows"
* #define PATH_SEPARATOR '\\'
* #define LINE_ENDING "\r\n"
* #define MAX_PATH_LENGTH 260
* #elif defined(__linux__) || defined(__APPLE__)
* #define PLATFORM_NAME "Unix/Linux"
* #define PATH_SEPARATOR '/'
* #define LINE_ENDING "\n"
* #define MAX_PATH_LENGTH 4096
* #else
* #define PLATFORM_NAME "Unknown"
* #define PATH_SEPARATOR '/'
* #define LINE_ENDING "\n"
* #define MAX_PATH_LENGTH 256
* #endif
*
*
* Exercise 7: Safe Memory Macros
* ------------------------------
* #define SAFE_MALLOC(ptr, type, count) do { \
* (ptr) = (type *)malloc((count) * sizeof(type)); \
* if ((ptr) == NULL) { \
* fprintf(stderr, "Memory allocation failed: %s:%d\n", \
* __FILE__, __LINE__); \
* exit(EXIT_FAILURE); \
* } \
* } while(0)
*
* #define SAFE_REALLOC(ptr, type, count) do { \
* type *temp = (type *)realloc((ptr), (count) * sizeof(type)); \
* if (temp == NULL) { \
* fprintf(stderr, "Reallocation failed: %s:%d\n", \
* __FILE__, __LINE__); \
* exit(EXIT_FAILURE); \
* } \
* (ptr) = temp; \
* } while(0)
*
* #define SAFE_FREE(ptr) do { \
* free(ptr); \
* (ptr) = NULL; \
* } while(0)
*
*
* Exercise 8: Enum and String Array (X-Macro)
* -------------------------------------------
* #define COLOR_LIST(X) \
* X(RED) \
* X(GREEN) \
* X(BLUE)
*
* #define ENUM_ENTRY(name) name,
* #define STRING_ENTRY(name) #name,
*
* enum Color { COLOR_LIST(ENUM_ENTRY) COLOR_COUNT };
* const char *Color_strings[] = { COLOR_LIST(STRING_ENTRY) };
*
*
* Exercise 9: Compile-Time Assertions
* -----------------------------------
* #define STATIC_ASSERT(cond, msg) \
* typedef char static_assertion_##msg[(cond) ? 1 : -1]
*
* // C11 alternative:
* // #define STATIC_ASSERT(cond, msg) _Static_assert(cond, #msg)
*
*
* Exercise 10: Complete Logging System
* ------------------------------------
* #ifdef ENABLE_LOGGING
* #ifdef LOG_SHOW_FILE
* #define LOG_FILE_INFO __FILE__ ":"
* #else
* #define LOG_FILE_INFO ""
* #endif
*
* #ifdef LOG_SHOW_LINE
* #define LOG_LINE_FMT "%d: "
* #define LOG_LINE_ARG , __LINE__
* #else
* #define LOG_LINE_FMT ""
* #define LOG_LINE_ARG
* #endif
*
* #define LOG(level, fmt, ...) do { \
* printf("[%s] " LOG_FILE_INFO LOG_LINE_FMT fmt "\n", \
* level LOG_LINE_ARG, ##__VA_ARGS__); \
* } while(0)
*
* #define LOG_ENTER() LOG("TRACE", "Entering %s", __func__)
* #define LOG_LEAVE() LOG("TRACE", "Leaving %s", __func__)
* #define LOG_VAR(var) LOG("DEBUG", #var " = %d", (var))
* #else
* #define LOG(level, fmt, ...)
* #define LOG_ENTER()
* #define LOG_LEAVE()
* #define LOG_VAR(var)
* #endif
*
* ============================================================ */