PY
C

Python to C

10 lessons

Progress0%
1Variables & Types2Functions3Lists → Arrays4Memory Management5String Handling6Structs7Preprocessor8File I/O9Pointers and Manual Memory10Build System: Makefile
All Mirror Courses
PY
C
Preprocessor
MirrorLesson 7 of 10
Lesson 7

Preprocessor

C preprocessor directives — includes, defines, conditional compilation

Introduction

In this lesson, you'll learn about preprocessor in C. Coming from Python, you already have a foundation for understanding this concept. We'll build on that knowledge while highlighting the key differences.

Mirror Card
PY
From Python:

In Python, you're familiar with c preprocessor directives — includes, defines, conditional compilation.

C
In C:

C has its own approach to c preprocessor directives — includes, defines, conditional compilation, which we'll explore step by step.

The C Way

Let's see how C handles this concept. Here's a typical example:

C
C Example
// #include — textual file insertion (like Python import)
#include <stdio.h>     // system header (angle brackets)
#include <stdlib.h>
#include "mymodule.h"  // local header (quotes)

// #define — symbolic constants (no type, no semicolon!)
#define MAX_SIZE 1024
#define PI 3.14159265358979

// Function-like macro (use parentheses everywhere!)
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define SQUARE(x) ((x) * (x))

// Include guard — prevents double-inclusion in headers
// mymodule.h:
#ifndef MYMODULE_H
#define MYMODULE_H

void my_function(void);
typedef struct { int x; } MyStruct;

#endif // MYMODULE_H

// Conditional compilation
#ifdef DEBUG
    #define LOG(msg) printf("[DEBUG] %s\n", msg)
#else
    #define LOG(msg)  // empty — compiled out
#endif

// Platform detection
#ifdef _WIN32
    #define PATH_SEP "\\"
#else
    #define PATH_SEP "/"
#endif

int main(void) {
    printf("max: %d\n", MAX_SIZE);
    int x = MAX(3, 7);   // expands inline
    LOG("starting");     // expands to printf or nothing
    return 0;
}

Comparing to Python

Here's how you might have written similar code in Python:

PY
Python (What you know)
# Imports
import os
import sys
from math import pi, sqrt
from typing import TYPE_CHECKING

# Constants
MAX_SIZE = 1024
DEBUG = False

# Conditional imports
if TYPE_CHECKING:
    from mymodule import MyClass  # only for type checkers

# Platform check
import platform
if platform.system() == "Windows":
    SEP = "\\"
else:
    SEP = "/"

# Version check
import sys
if sys.version_info < (3, 10):
    raise RuntimeError("Need Python 3.10+")
Mirror Card
PY
From Python:

You may be used to different syntax or behavior.

C
In C:

#include is textual insertion — not a scoped import like Python's import

Mirror Card
PY
From Python:

You may be used to different syntax or behavior.

C
In C:

#define creates text substitutions — no type checking, no scoping

Mirror Card
PY
From Python:

You may be used to different syntax or behavior.

C
In C:

Include guards (#ifndef/#define/#endif) prevent double-inclusion

Mirror Card
PY
From Python:

You may be used to different syntax or behavior.

C
In C:

Conditional compilation (#ifdef) removes code at compile time — not runtime if/else

Mirror Card
PY
From Python:

You may be used to different syntax or behavior.

C
In C:

Function-like macros are dangerous — always parenthesize arguments and result

Step-by-Step Breakdown

1. #include

The preprocessor literally copies the content of the included file. Angle brackets search system paths; quotes search locally first.

PY
Python
import os
from math import sqrt
C
C
#include <math.h>     // system: /usr/include/math.h
#include "utils.h"    // local: ./utils.h then system
Common Pitfall
Without include guards or #pragma once, the same header can be included multiple times, causing 'redefinition' errors.

2. #define Constants

#define creates simple text substitutions. Use ALL_CAPS by convention. Add no semicolon — the preprocessor doesn't understand C syntax.

PY
Python
MAX_SIZE = 1024  # Python constant (by convention)
C
C
#define MAX_SIZE 1024
// Better (typed): const int MAX_SIZE = 1024; // C99+
// or: enum { MAX_SIZE = 1024 };
Rule of Thumb
Prefer const int or enum over #define for typed constants — they have type safety and scope.

3. Include Guards

Include guards prevent a header from being processed twice. Every .h file should have one.

PY
Python
# Python: no double-import problem — modules are cached
C
C
// top of every .h file:
#ifndef MY_HEADER_H
#define MY_HEADER_H

// ... declarations ...

#endif
// Or: #pragma once (non-standard but widely supported)

4. Conditional Compilation

#ifdef removes entire code blocks at compile time. Use for debug builds, platform-specific code, or feature flags.

PY
Python
DEBUG = os.getenv("DEBUG", "0") == "1"
if DEBUG:
    print("debug info")
C
C
// Compile with: gcc -DDEBUG main.c
#ifdef DEBUG
    printf("x = %d\n", x);
#endif

// Platform-specific
#ifdef _WIN32
    windows_specific_code();
#elif __linux__
    linux_specific_code();
#endif

Common Mistakes

When coming from Python, developers often make these mistakes:

  • #include is textual insertion — not a scoped import like Python's import
  • #define creates text substitutions — no type checking, no scoping
  • Include guards (#ifndef/#define/#endif) prevent double-inclusion
Common Pitfall
Don't assume C works exactly like Python. While the concepts may be similar, the syntax and behavior can differ significantly.

Key Takeaways

  • #include copies file content; angle brackets for system, quotes for local
  • #define creates untyped text substitutions; prefer const/enum for typed constants
  • Include guards (#ifndef/#define/#endif) in every .h file to prevent double-inclusion
  • #ifdef for conditional compilation — code removed entirely, not just skipped at runtime
Rule of Thumb
The best way to learn is by doing. Try rewriting some of your Python code in C to practice these concepts.
PreviousNext