c
exercises
exercises.c🔧c
/**
* =============================================================================
* Macros and Definitions in C - Exercises
* =============================================================================
*
* Practice exercises for understanding and applying C preprocessor macros.
* Complete each exercise in the designated space.
*
* Compile: gcc -Wall -Wextra -std=c99 -o exercises exercises.c
* Run: ./exercises
* =============================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
// =============================================================================
// Exercise 1: Basic Object-like Macros
// =============================================================================
/**
* Define the following macros:
* 1. GRAVITY - acceleration due to gravity (9.81 m/s²)
* 2. SPEED_OF_LIGHT - speed of light in m/s (299792458)
* 3. MAX_USERNAME_LEN - maximum username length (32)
* 4. DEFAULT_TIMEOUT - default timeout in seconds (30)
* 5. APP_NAME - application name as "MacroExercises"
*
* Then use them in the function below to print their values.
*/
// YOUR CODE HERE: Define the macros
void exercise1(void) {
printf("=== Exercise 1: Basic Object-like Macros ===\n");
// YOUR CODE HERE: Print the values of each macro
// Example: printf("Gravity: %.2f m/s²\n", GRAVITY);
printf("\n");
}
// =============================================================================
// Exercise 2: Function-like Macros for Math Operations
// =============================================================================
/**
* Define the following function-like macros:
* 1. SQUARE(x) - returns x squared
* 2. AVERAGE(a, b) - returns average of a and b
* 3. IS_EVEN(n) - returns 1 if n is even, 0 otherwise
* 4. IS_POSITIVE(x) - returns 1 if x > 0, 0 otherwise
* 5. CELSIUS_TO_FAHRENHEIT(c) - converts Celsius to Fahrenheit (F = C * 9/5 + 32)
*
* Remember to use proper parentheses!
*/
// YOUR CODE HERE: Define the macros
void exercise2(void) {
printf("=== Exercise 2: Function-like Macros ===\n");
// Test SQUARE
printf("SQUARE(5) = %d\n", 0); // Should be 25
printf("SQUARE(3 + 2) = %d\n", 0); // Should be 25, not 11
// Test AVERAGE
printf("AVERAGE(10, 20) = %.2f\n", 0.0); // Should be 15.0
// Test IS_EVEN
printf("IS_EVEN(4) = %d\n", 0); // Should be 1
printf("IS_EVEN(7) = %d\n", 0); // Should be 0
// Test IS_POSITIVE
printf("IS_POSITIVE(5) = %d\n", 0); // Should be 1
printf("IS_POSITIVE(-3) = %d\n", 0); // Should be 0
// Test CELSIUS_TO_FAHRENHEIT
printf("CELSIUS_TO_FAHRENHEIT(0) = %.2f\n", 0.0); // Should be 32.0
printf("CELSIUS_TO_FAHRENHEIT(100) = %.2f\n", 0.0); // Should be 212.0
printf("\n");
}
// =============================================================================
// Exercise 3: Multi-statement Macros
// =============================================================================
/**
* Define the following multi-statement macros using do { } while(0):
*
* 1. SWAP_DOUBLE(a, b) - swaps two double values
*
* 2. PRINT_RANGE(start, end) - prints numbers from start to end (inclusive)
*
* 3. SAFE_STRCPY(dest, src, size) - safely copies string with size limit
* and ensures null termination
*/
// YOUR CODE HERE: Define the macros
void exercise3(void) {
printf("=== Exercise 3: Multi-statement Macros ===\n");
// Test SWAP_DOUBLE
double x = 3.14, y = 2.71;
printf("Before swap: x = %.2f, y = %.2f\n", x, y);
// SWAP_DOUBLE(x, y); // Uncomment after implementing
printf("After swap: x = %.2f, y = %.2f\n", x, y);
printf("\n");
// Test PRINT_RANGE
printf("PRINT_RANGE(1, 5): ");
// PRINT_RANGE(1, 5); // Uncomment after implementing
printf("\n\n");
// Test SAFE_STRCPY
char buffer[10];
const char *long_string = "This is a very long string";
// SAFE_STRCPY(buffer, long_string, sizeof(buffer)); // Uncomment
printf("SAFE_STRCPY result: %s\n", buffer);
printf("\n");
}
// =============================================================================
// Exercise 4: Stringification (#) Operator
// =============================================================================
/**
* Define the following macros using stringification:
*
* 1. VAR_TO_STRING(var) - converts variable name to string
*
* 2. PRINT_INT(var) - prints "var = <value>" for an integer variable
*
* 3. PRINT_DOUBLE(var) - prints "var = <value>" for a double variable
*
* 4. ASSERT_TRUE(cond) - if cond is false, prints the condition as string
* and the file/line number
*/
// YOUR CODE HERE: Define the macros
void exercise4(void) {
printf("=== Exercise 4: Stringification Operator ===\n");
int count = 42;
double pi = 3.14159;
// Test VAR_TO_STRING
// printf("Variable name: %s\n", VAR_TO_STRING(count));
// Test PRINT_INT
// PRINT_INT(count);
// Test PRINT_DOUBLE
// PRINT_DOUBLE(pi);
// Test ASSERT_TRUE
int value = 10;
// ASSERT_TRUE(value > 0); // Should pass silently
// ASSERT_TRUE(value > 100); // Should print failure message
printf("\n");
}
// =============================================================================
// Exercise 5: Token Pasting (##) Operator
// =============================================================================
/**
* Define the following macros using token pasting:
*
* 1. MAKE_FUNCTION_NAME(prefix, suffix) - creates a function name
* Example: MAKE_FUNCTION_NAME(calc, sum) -> calc_sum
*
* 2. DECLARE_COUNTER(name) - declares a static int counter named name_counter
* and a function increment_name() that increments and returns it
*
* 3. CREATE_PAIR(type, name) - creates two variables: name_first and name_second
*/
// YOUR CODE HERE: Define the macros
void exercise5(void) {
printf("=== Exercise 5: Token Pasting Operator ===\n");
// Test CREATE_PAIR
// CREATE_PAIR(int, coord);
// coord_first = 10;
// coord_second = 20;
// printf("Coordinates: (%d, %d)\n", coord_first, coord_second);
// Test DECLARE_COUNTER (would need to be outside function)
// Call increment function and print result
printf("\n");
}
// =============================================================================
// Exercise 6: Variadic Macros
// =============================================================================
/**
* Define the following variadic macros:
*
* 1. PRINT(...) - simple printf wrapper
*
* 2. LOG_MESSAGE(level, format, ...) - prints "[LEVEL] message"
* where level is a string like "INFO", "WARN", "ERROR"
*
* 3. DEBUG_PRINT(format, ...) - prints debug message with file and line
* Format: "[DEBUG file:line] message"
*/
// YOUR CODE HERE: Define the macros
void exercise6(void) {
printf("=== Exercise 6: Variadic Macros ===\n");
// Test PRINT
// PRINT("Hello, World!\n");
// PRINT("Value: %d\n", 42);
// Test LOG_MESSAGE
// LOG_MESSAGE("INFO", "Application started");
// LOG_MESSAGE("ERROR", "Failed to open file: %s", "test.txt");
// Test DEBUG_PRINT
// DEBUG_PRINT("Processing item %d of %d", 5, 10);
printf("\n");
}
// =============================================================================
// Exercise 7: Array Utility Macros
// =============================================================================
/**
* Define the following array utility macros:
*
* 1. ARRAY_LEN(arr) - returns the number of elements in an array
*
* 2. ARRAY_LAST(arr) - returns the last element of an array
*
* 3. ARRAY_FIRST(arr) - returns the first element of an array
*
* 4. IN_RANGE(val, min, max) - returns 1 if val is between min and max (inclusive)
*
* 5. PRINT_ARRAY_INT(arr, size) - prints all elements of an int array
*/
// YOUR CODE HERE: Define the macros
void exercise7(void) {
printf("=== Exercise 7: Array Utility Macros ===\n");
int numbers[] = {10, 20, 30, 40, 50};
// Test ARRAY_LEN
// printf("Array length: %zu\n", ARRAY_LEN(numbers));
// Test ARRAY_FIRST and ARRAY_LAST
// printf("First element: %d\n", ARRAY_FIRST(numbers));
// printf("Last element: %d\n", ARRAY_LAST(numbers));
// Test IN_RANGE
int value = 25;
// printf("%d in range [20, 30]: %d\n", value, IN_RANGE(value, 20, 30));
// printf("%d in range [1, 10]: %d\n", value, IN_RANGE(value, 1, 10));
// Test PRINT_ARRAY_INT
// printf("Array: ");
// PRINT_ARRAY_INT(numbers, ARRAY_LEN(numbers));
printf("\n");
}
// =============================================================================
// Exercise 8: Bit Manipulation Macros
// =============================================================================
/**
* Define the following bit manipulation macros:
*
* 1. BIT_SET(n, pos) - sets bit at position pos in n
* 2. BIT_CLEAR(n, pos) - clears bit at position pos in n
* 3. BIT_TOGGLE(n, pos) - toggles bit at position pos in n
* 4. BIT_CHECK(n, pos) - returns 1 if bit at pos is set, 0 otherwise
* 5. BITMASK(n) - creates a bitmask with n lower bits set (e.g., BITMASK(3) = 0b111 = 7)
*/
// YOUR CODE HERE: Define the macros
void exercise8(void) {
printf("=== Exercise 8: Bit Manipulation Macros ===\n");
unsigned char value = 0b00001010; // 10 in decimal
printf("Original value: %d (0b%08b)\n", value, value);
// Test BIT_SET
// printf("After BIT_SET(value, 0): %d\n", BIT_SET(value, 0)); // Should be 11
// Test BIT_CLEAR
// printf("After BIT_CLEAR(value, 1): %d\n", BIT_CLEAR(value, 1)); // Should be 8
// Test BIT_TOGGLE
// printf("After BIT_TOGGLE(value, 3): %d\n", BIT_TOGGLE(value, 3)); // Should be 2
// Test BIT_CHECK
// printf("BIT_CHECK(value, 1): %d\n", BIT_CHECK(value, 1)); // Should be 1
// printf("BIT_CHECK(value, 0): %d\n", BIT_CHECK(value, 0)); // Should be 0
// Test BITMASK
// printf("BITMASK(4): %d\n", BITMASK(4)); // Should be 15 (0b1111)
printf("\n");
}
// =============================================================================
// Exercise 9: Safe Memory Macros
// =============================================================================
/**
* Define the following memory-related macros:
*
* 1. SAFE_MALLOC(ptr, type, count) - allocates memory and checks for NULL
* If allocation fails, prints error and sets ptr to NULL
*
* 2. SAFE_REALLOC(ptr, type, count) - reallocates memory safely
*
* 3. SAFE_FREE(ptr) - frees memory and sets pointer to NULL
*
* 4. ZERO_STRUCT(s) - zeros out a structure
*/
// YOUR CODE HERE: Define the macros
void exercise9(void) {
printf("=== Exercise 9: Safe Memory Macros ===\n");
// Test SAFE_MALLOC
int *arr = NULL;
// SAFE_MALLOC(arr, int, 5);
if (arr) {
for (int i = 0; i < 5; i++) {
arr[i] = (i + 1) * 10;
}
printf("Allocated array: ");
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
// Test SAFE_FREE
// SAFE_FREE(arr);
// printf("After SAFE_FREE, arr is: %p\n", (void *)arr);
}
// Test ZERO_STRUCT
struct Point {
int x, y, z;
} point = {10, 20, 30};
printf("Before ZERO_STRUCT: (%d, %d, %d)\n", point.x, point.y, point.z);
// ZERO_STRUCT(point);
printf("After ZERO_STRUCT: (%d, %d, %d)\n", point.x, point.y, point.z);
printf("\n");
}
// =============================================================================
// Exercise 10: Build Your Own Assert System
// =============================================================================
/**
* Create a comprehensive assertion system with these macros:
*
* 1. ASSERT(cond) - basic assertion that prints condition and location
*
* 2. ASSERT_EQ(a, b) - asserts that a equals b
*
* 3. ASSERT_NE(a, b) - asserts that a does not equal b
*
* 4. ASSERT_GT(a, b) - asserts that a is greater than b
*
* 5. ASSERT_NULL(ptr) - asserts that ptr is NULL
*
* 6. ASSERT_NOT_NULL(ptr) - asserts that ptr is not NULL
*
* All assertions should:
* - Print the file and line number on failure
* - Print the expression that failed
* - Not terminate the program (soft assertions)
*/
// YOUR CODE HERE: Define the macros
void exercise10(void) {
printf("=== Exercise 10: Custom Assert System ===\n");
int a = 10, b = 20;
// Test assertions
// ASSERT(a > 0); // Should pass
// ASSERT(a > 100); // Should fail
// ASSERT_EQ(a, 10); // Should pass
// ASSERT_EQ(a, b); // Should fail
// ASSERT_NE(a, b); // Should pass
// ASSERT_NE(a, 10); // Should fail
// ASSERT_GT(b, a); // Should pass
// ASSERT_GT(a, b); // Should fail
int *ptr = NULL;
int *valid_ptr = &a;
// ASSERT_NULL(ptr); // Should pass
// ASSERT_NULL(valid_ptr); // Should fail
// ASSERT_NOT_NULL(valid_ptr); // Should pass
// ASSERT_NOT_NULL(ptr); // Should fail
printf("\n");
}
// =============================================================================
// Main Function
// =============================================================================
int main(void) {
printf("============================================================\n");
printf(" MACROS AND DEFINITIONS - EXERCISES \n");
printf("============================================================\n\n");
exercise1();
exercise2();
exercise3();
exercise4();
exercise5();
exercise6();
exercise7();
exercise8();
exercise9();
exercise10();
printf("============================================================\n");
printf("Complete the exercises by implementing the required macros!\n");
printf("============================================================\n");
return 0;
}
// =============================================================================
// ANSWER KEY
// =============================================================================
/*
* Below are the solutions. Try to solve the exercises yourself first!
*/
/*
// Exercise 1: Basic Object-like Macros
#define GRAVITY 9.81
#define SPEED_OF_LIGHT 299792458
#define MAX_USERNAME_LEN 32
#define DEFAULT_TIMEOUT 30
#define APP_NAME "MacroExercises"
// Exercise 2: Function-like Macros
#define SQUARE(x) ((x) * (x))
#define AVERAGE(a, b) (((a) + (b)) / 2.0)
#define IS_EVEN(n) (((n) % 2) == 0)
#define IS_POSITIVE(x) ((x) > 0)
#define CELSIUS_TO_FAHRENHEIT(c) (((c) * 9.0 / 5.0) + 32)
// Exercise 3: Multi-statement Macros
#define SWAP_DOUBLE(a, b) do { \
double _temp = (a); \
(a) = (b); \
(b) = _temp; \
} while(0)
#define PRINT_RANGE(start, end) do { \
for (int _i = (start); _i <= (end); _i++) { \
printf("%d ", _i); \
} \
} while(0)
#define SAFE_STRCPY(dest, src, size) do { \
strncpy((dest), (src), (size) - 1); \
(dest)[(size) - 1] = '\0'; \
} while(0)
// Exercise 4: Stringification Operator
#define VAR_TO_STRING(var) #var
#define PRINT_INT(var) printf(#var " = %d\n", (var))
#define PRINT_DOUBLE(var) printf(#var " = %.6f\n", (var))
#define ASSERT_TRUE(cond) do { \
if (!(cond)) { \
printf("Assertion failed: %s\n", #cond); \
printf(" File: %s, Line: %d\n", __FILE__, __LINE__); \
} \
} while(0)
// Exercise 5: Token Pasting Operator
#define MAKE_FUNCTION_NAME(prefix, suffix) prefix##_##suffix
#define DECLARE_COUNTER(name) \
static int name##_counter = 0; \
int increment_##name(void) { return ++name##_counter; }
#define CREATE_PAIR(type, name) type name##_first, name##_second
// Exercise 6: Variadic Macros
#define PRINT(...) printf(__VA_ARGS__)
#define LOG_MESSAGE(level, format, ...) \
printf("[%s] " format "\n", level, ##__VA_ARGS__)
#define DEBUG_PRINT(format, ...) \
printf("[DEBUG %s:%d] " format "\n", __FILE__, __LINE__, ##__VA_ARGS__)
// Exercise 7: Array Utility Macros
#define ARRAY_LEN(arr) (sizeof(arr) / sizeof((arr)[0]))
#define ARRAY_LAST(arr) ((arr)[ARRAY_LEN(arr) - 1])
#define ARRAY_FIRST(arr) ((arr)[0])
#define IN_RANGE(val, min, max) ((val) >= (min) && (val) <= (max))
#define PRINT_ARRAY_INT(arr, size) do { \
printf("["); \
for (size_t _i = 0; _i < (size); _i++) { \
if (_i > 0) printf(", "); \
printf("%d", (arr)[_i]); \
} \
printf("]\n"); \
} while(0)
// Exercise 8: Bit Manipulation Macros
#define BIT_SET(n, pos) ((n) | (1 << (pos)))
#define BIT_CLEAR(n, pos) ((n) & ~(1 << (pos)))
#define BIT_TOGGLE(n, pos) ((n) ^ (1 << (pos)))
#define BIT_CHECK(n, pos) (((n) >> (pos)) & 1)
#define BITMASK(n) ((1 << (n)) - 1)
// Exercise 9: Safe Memory Macros
#define SAFE_MALLOC(ptr, type, count) do { \
(ptr) = (type *)malloc(sizeof(type) * (count)); \
if ((ptr) == NULL) { \
fprintf(stderr, "Memory allocation failed at %s:%d\n", \
__FILE__, __LINE__); \
} \
} while(0)
#define SAFE_REALLOC(ptr, type, count) do { \
type *_new_ptr = (type *)realloc((ptr), sizeof(type) * (count)); \
if (_new_ptr == NULL) { \
fprintf(stderr, "Reallocation failed at %s:%d\n", \
__FILE__, __LINE__); \
} else { \
(ptr) = _new_ptr; \
} \
} while(0)
#define SAFE_FREE(ptr) do { \
if ((ptr) != NULL) { \
free(ptr); \
(ptr) = NULL; \
} \
} while(0)
#define ZERO_STRUCT(s) memset(&(s), 0, sizeof(s))
// Exercise 10: Assert System
#define ASSERT(cond) do { \
if (!(cond)) { \
fprintf(stderr, "ASSERT FAILED: %s\n", #cond); \
fprintf(stderr, " Location: %s:%d\n", __FILE__, __LINE__); \
} \
} while(0)
#define ASSERT_EQ(a, b) do { \
if ((a) != (b)) { \
fprintf(stderr, "ASSERT_EQ FAILED: %s == %s\n", #a, #b); \
fprintf(stderr, " Location: %s:%d\n", __FILE__, __LINE__); \
} \
} while(0)
#define ASSERT_NE(a, b) do { \
if ((a) == (b)) { \
fprintf(stderr, "ASSERT_NE FAILED: %s != %s\n", #a, #b); \
fprintf(stderr, " Location: %s:%d\n", __FILE__, __LINE__); \
} \
} while(0)
#define ASSERT_GT(a, b) do { \
if (!((a) > (b))) { \
fprintf(stderr, "ASSERT_GT FAILED: %s > %s\n", #a, #b); \
fprintf(stderr, " Location: %s:%d\n", __FILE__, __LINE__); \
} \
} while(0)
#define ASSERT_NULL(ptr) do { \
if ((ptr) != NULL) { \
fprintf(stderr, "ASSERT_NULL FAILED: %s is not NULL\n", #ptr); \
fprintf(stderr, " Location: %s:%d\n", __FILE__, __LINE__); \
} \
} while(0)
#define ASSERT_NOT_NULL(ptr) do { \
if ((ptr) == NULL) { \
fprintf(stderr, "ASSERT_NOT_NULL FAILED: %s is NULL\n", #ptr); \
fprintf(stderr, " Location: %s:%d\n", __FILE__, __LINE__); \
} \
} while(0)
*/