Python for C Programmers: From printf to print in One Afternoon
TL;DR: Almost everything you manually manage in C is automatic in Python: memory (garbage-collected), string length (
len(s), no\0), array growth (list.append), key-value lookup (dict, no hand-rolled hash table).printf("x = %d", x)becomesprint(f"x = {x}"). What's genuinely new: slicing, negative indexing, and iteration without index variables. Every example below runs in the browser in Cubemate — no compiler, no makefile.
If you learned C first — as most engineering students do — you know what a string really is, why arrays have fixed sizes, and what happens when you read past the end of one. That knowledge doesn't become useless in Python; it becomes your advantage. You'll understand why Python's conveniences cost what they cost, while everyone else just uses them blindly.
But first, enjoy the disappearing act.
Hello world, and where printf went
x = 5
pi = 3.14159
name = "Cubemate"
print(f"x = {x}") # printf("x = %d\n", x)
print(f"pi = {pi:.2f}") # printf("pi = %.2f\n", pi)
print(f"{name} has {len(name)} chars")
Three things happened: no type declarations (the value carries the type), no \n (print adds it), and format specifiers moved inside the braces — {pi:.2f} is your %.2f. There is no compilation step; the file runs top to bottom, main() optional.
Strings: no char arrays, no \0, no strcpy
C strings are the hardest part of early C. Python strings are the easiest part of Python:
s = "hello world"
print(len(s)) # strlen — no scanning for \0
t = s # "copy" — no strcpy, no buffer sizing
u = s + "!" # concatenation — no strcat, no overflow
print(s[0], s[-1]) # first and LAST char — negative indexing
print(s[0:5]) # "hello" — substring as syntax
print(s.upper()) # methods live on the string itself
One real difference to internalize: Python strings are immutable. s[0] = "H" is an error — operations like .upper() return new strings. Coming from C, think of every Python string as const char * that manages its own memory.
Arrays that grow: int arr[10] → list
The fixed-size array and its hand-tracked length are replaced by list:
nums = [10, 20, 30] # no size declared
nums.append(40) # grows itself — no realloc
nums.remove(20) # delete by value, elements shift
print(len(nums)) # length is always known
print(nums[1:3]) # slice: elements 1 and 2
print(nums[::-1]) # reversed copy, one expression
for n in nums: # no index variable needed
print(n)
The habit to drop: for i in range(len(nums)) with nums[i] inside — that's the C reflex. Python iterates over values directly; when you genuinely need the index too, for i, n in enumerate(nums): gives you both.
Under the hood — and your C brain will want to know — a Python list is a dynamic array of pointers with amortized-O(1) append. Your instincts about contiguous memory and reallocation costs still apply.
The hash table you don't have to write
Every C programmer has either written a hash table or carefully avoided needing one. Python ships it as a literal:
ages = {"asha": 21, "rahul": 23}
ages["meera"] = 22 # insert / update
print(ages["asha"]) # lookup
print("kiran" in ages) # membership test
ages.pop("rahul") # delete
for name, age in ages.items():
print(name, age)
That's the dict — the single most used type in Python after strings. O(1) average lookup, any hashable type as key, no collision handling on your side of the API.
Structs → tuples, dicts, and dataclasses
# Quick grouping — a tuple (fixed, ordered, immutable):
point = (3, 4)
x, y = point # unpacking
# A real struct equivalent — dataclass:
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
p = Point(3, 4)
print(p) # Point(x=3, y=4) — printing for free
print(p.x + p.y)
The dataclass is your struct with benefits: named fields, defaults, readable printing, and equality comparison, all generated from the three-line declaration. And where C has no built-in set, Python does: {1, 2, 3} — deduplication and O(1) membership without building a lookup table yourself.
Control flow: familiar shapes, lighter syntax
a, b = 5, 2
if a > 0 and b != 0: # && → and, || → or, ! → not
print(a / b) # true division: 2.5
print(a // b) # C-style integer division: 2
elif a == 0:
print("zero")
else:
print("negative")
count = 0
while count < 3: # while, break, continue: unchanged
count += 1 # note: no ++ in Python
Watch the division: / between two ints gives a float in Python — 5 / 2 is 2.5, not 2. When you want C's truncating behavior, that's //. This one bites every C programmer exactly once.
Blocks are defined by indentation — the braces are gone, and the compiler-argument about where they belong is over. Also gone: ++/-- (use += 1) and switch (Python 3.10+ has match, or use if/elif).
Functions, and the memory you no longer manage
def stats(values): # no return type, no prototypes
total = sum(values)
return total, total / len(values) # returning two values — no out-params
total, avg = stats([10, 20, 30])
print(total, avg)
Multiple return values replace the pointer-out-parameter pattern entirely. And the biggest absence of all: there is no malloc, no free, no ownership tracking — objects are garbage-collected when nothing references them. Your hard-won discipline about object lifetimes doesn't disappear; it resurfaces later as intuition for why long-lived references cause memory growth.
Run the comparison, don't just read it
The fastest way through this mapping is executing both sides: Cubemate runs C and Python in the same browser sandbox, so you can paste a C snippet, write its Python twin, and compare actual output — no gcc, no environment setup. When one topic deserves depth (dataclasses and slicing usually do), ask Bookmate for a tutorial like "Python collections for a C programmer" and work through a chaptered, runnable book on exactly that gap.
For the general switching method — mapping, idiom traps, and a two-week plan that works for any language pair — see How to Learn a Second Programming Language.
Frequently asked questions
Is Python easy to learn after C?
Yes — often easier than for anyone else, because C teaches you what's really happening underneath. The things that consume most C effort (memory management, string handling, array bookkeeping) simply disappear, and the core logic skills transfer directly. Most C programmers are productive in Python within a week.
What is the Python equivalent of printf?
The print() function with f-strings: printf("x = %d\n", x) becomes print(f"x = {x}"). Format control like %.2f becomes {value:.2f} inside the braces, and print adds the newline for you.
Does Python have pointers?
Not exposed ones. Every Python variable is a reference to an object (closer to a pointer than a C value variable), but there's no pointer arithmetic, no dereferencing, and no manual allocation — memory is garbage-collected automatically.
What replaces C structs in Python?
For quick grouping, tuples ((x, y)) or dictionaries. For named, typed record shapes like a C struct, Python's dataclasses give you fields with names and defaults in three lines, plus printing and comparison for free.
Why do engineering students learn C first and Python second?
C teaches how computers actually work — memory, addresses, the cost of things. Python teaches how to build quickly. The combination is powerful: C programmers who learn Python keep their mental model of what's expensive while gaining a language where ideas become programs in minutes.
Ready to start coding?
Write, run, and learn code in your browser — Python, Java, Go, Rust and more. No installation, no setup. Free 7-day trial.
Start Free