c

exercises

exercises.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
 * 
 * ============================================================ */
Exercises - C Programming Tutorial | DeepML