Chapter 15: Advanced Topics (Introduction)
These are the features that make C a very powerful and flexible language. Most beginners skip them, but once you understand these, you can write professional-level code, make your programs more efficient, and understand real-world C code (like Linux kernel or game engines).
We will cover:
- Preprocessor directives
- Macros
- Enums
- Bit fields
- Typedef
- Command-line arguments
Let’s go one by one.
1. Preprocessor Directives
These are instructions for the preprocessor (the first stage of compilation). They start with # and are processed before the actual compilation.
Common directives:
#include – Include header files
|
0 1 2 3 4 5 6 7 |
#include <stdio.h> // System header (angle brackets) #include "myheader.h" // User-defined header (double quotes) |
#define – Define constants or macros
|
0 1 2 3 4 5 6 7 |
#define PI 3.14159 #define MAX_STUDENTS 100 |
#undef – Undefine a previously defined macro
|
0 1 2 3 4 5 6 |
#undef PI |
#ifdef / #ifndef / #endif – Conditional compilation
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#define DEBUG #ifdef DEBUG printf("Debug mode is ON\n"); #endif #ifndef DEBUG printf("Debug mode is OFF\n"); #endif |
#if / #else / #elif – More advanced conditions
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
#if VERSION == 2 #define FEATURE_X #elif VERSION == 3 #define FEATURE_Y #else #define BASIC_VERSION #endif |
2. Macros
Macros are like text replacements done by the preprocessor.
Simple constant macro
|
0 1 2 3 4 5 6 7 |
#define PI 3.14159 printf("Pi = %f\n", PI); // Replaced with 3.14159 |
Function-like macro (very powerful!)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
#define SQUARE(x) ((x) * (x)) int main() { int num = 5; printf("Square = %d\n", SQUARE(num)); // ((5) * (5)) → 25 printf("Square of 3+2 = %d\n", SQUARE(3+2)); // ((3+2) * (3+2)) → 25 return 0; } |
Best practice: Always put parentheses around arguments and whole expression – prevents bugs!
Bad macro (can cause wrong result):
|
0 1 2 3 4 5 6 7 |
#define SQUARE(x) x * x SQUARE(3+2) → 3+2 * 3+2 → 3+6+2 → 11 (wrong!) |
3. Enums (Enumerations)
Enums let you create a set of named integer constants – makes code readable.
Syntax:
|
0 1 2 3 4 5 6 |
enum enum_name { constant1, constant2, ... }; |
Example – Days of the week
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#include <stdio.h> enum Days { MONDAY = 1, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY }; int main() { enum Days today = WEDNESDAY; printf("Today is day number: %d\n", today); // 3 if (today == SUNDAY) { printf("It's holiday!\n"); } else { printf("Work day.\n"); } return 0; } |
Output:
|
0 1 2 3 4 5 6 7 |
Today is day number: 3 Work day. |
4. Bit Fields
Bit fields let you pack multiple small integer values into one integer – saves memory.
Syntax:
|
0 1 2 3 4 5 6 7 8 |
struct { type member_name : number_of_bits; } variable; |
Example – Packing student flags
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
#include <stdio.h> struct StudentFlags { unsigned int isPresent : 1; // 1 bit (0 or 1) unsigned int hasPaidFees : 1; // 1 bit unsigned int isHosteller : 1; // 1 bit unsigned int gender : 2; // 2 bits (0-3) unsigned int year : 3; // 3 bits (0-7) }; int main() { struct StudentFlags s1; s1.isPresent = 1; s1.hasPaidFees = 1; s1.isHosteller = 0; s1.gender = 1; // 1 = Male, 0 = Female s1.year = 3; printf("Size of flags: %zu bytes\n", sizeof(s1)); // Usually 4 bytes instead of 5+ ints printf("Is present? %d\n", s1.isPresent); printf("Year: %d\n", s1.year); return 0; } |
Use cases:
- Network packets
- Hardware registers
- Flags in embedded systems
5. Typedef
typedef creates an alias (new name) for existing data types – makes code cleaner.
Syntax:
|
0 1 2 3 4 5 6 |
typedef existing_type new_name; |
Examples:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
typedef unsigned long long ull; // Now ull means unsigned long long typedef struct { char name[50]; int roll; float marks; } Student; // Now Student is the type typedef int* IntPtr; // IntPtr is pointer to int |
Very common in real code:
|
0 1 2 3 4 5 6 7 |
typedef unsigned char byte; // byte means unsigned char byte data[1024]; // Cleaner than unsigned char data[1024]; |
6. Command-Line Arguments
Your program can accept arguments when you run it from terminal/command prompt.
Syntax in main():
|
0 1 2 3 4 5 6 |
int main(int argc, char *argv[]) |
- argc = argument count (including program name)
- argv = array of strings (argument values)
Example:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <stdio.h> int main(int argc, char *argv[]) { printf("Total arguments: %d\n", argc); printf("Program name: %s\n", argv[0]); for (int i = 1; i < argc; i++) { printf("Argument %d: %s\n", i, argv[i]); } return 0; } |
How to run:
|
0 1 2 3 4 5 6 7 |
gcc program.c -o myprog ./myprog hello world 123 |
Output:
|
0 1 2 3 4 5 6 7 8 9 10 |
Total arguments: 4 Program name: ./myprog Argument 1: hello Argument 2: world Argument 3: 123 |
Real use:
- ./calculator 10 + 20 → argv[1] = “10”, argv[2] = “+”, argv[3] = “20”
Today’s Homework
- Create a macro MAX(a,b) that returns the larger of two numbers.
- Define an enum for months (JAN to DEC) and print the month name based on user input.
- Use typedef to create alias for a structure Employee (name, id, salary).
- Write a program that uses command-line arguments to add two numbers (e.g., ./add 15 25 should print 40).
- Create a structure with bit fields for flags (like isAdmin:1, isActive:1, role:3) and print their values.
