What you're about to read is Computer Science the fast way. This is designed to take complete beginners and dive them into the heart of what C programming is and introduce some ideas from Computer Science, without them drowning in the process.
If you want a pretty detailed history of those events that led to the creation of this field, check out the history section of CS1.
That section is designed to talk about the humans behind the tech. They had (and some of them, given that they are still alive, still do) a sould and stuff you know? Knowing those details enriches you in a way to cold documentation ever will.
For the uninitiated, let us begin from the very basics.
You use one every single day (in some for or another). But if I stopped you on the street and asked you to actually explain what a computer is, could you? Most people freeze. Let's make sure you're not most people. (see what I did here? I just recycled the resistor trick from E1; peak creativity).
If we were to put the answer to that question into the simplest terms a computer is a machine that follows instructions to mess around with information. We ain't messing around with what information MEANS today (although a blogpost on that is incoming after the bootcamp. But it works along the lines of: something goes in (you press a key, you click, you talk into the microphone), the machine does some work on it, and something comes OUT (a letter appears, a video plays, a game reacts). In -> work -> out. That's the whole job, right at the bottom.
Now here's the part nobody tells you, and it's going to follow you for the rest of this course:
a computer is incredibly, almost insultingly, DUMB.
It feels clever, sure. It plays your music and runs your games and lets you talk to your friends. But under the hood it only knows how to do a tiny handful of laughably simple things like adding two numbers, comparing two numbers, moving a number from one spot to another. That's basically it. The trick is that it does those baby steps billions of times a second and never once gets tired or bored and very rarely gets something wrong or misses. Every "smart" thing a computer appears to do is really just millions of those dumb little steps piled on top of each other.
Myth: "The computer is smart, it understands what I want."
WRONG. The computer understands NOTHING. It follows your instructions to the letter, even when your instructions are nonsense. Tattoo this somewhere (preferably the inside if your eyelids): when the instruction you give the computer misbehave, it is almost never the computer being "buggy", it's the computer doing EXACTLY what you told it, which just happened to not be what you really meant. It's not the machine's fault. (It's yours. Sorry =)) )
Let's settle the score about this: when I say "computer" I mean any type of computing device. Could be a phone, a laptop or your PS5, as far as the branch of Computer Science is concerned, they're all just computing machines that work on roughly the same rules.
For the purposes of this course, you can envision, whenever you see the word "computer" a literal desktop PC. It's easier that way.

So what's actually inside the box? You don't need to know every part. You need to know what each main piece is for. There are really only a few that matter:
The most common mix-up, killed right now: memory (RAM) and storage (disk) are NOT the same thing. Memory is the small, fast, temporary desk you work on. Storage is the big, slow, permanent basement you keep things in. "I've got 16 GB of RAM" and "I've got 512 GB of storage" are talking about two totally different parts of the machine. Burn that distinction in now and save yourself a world of confusion.
If a picture helps, imagine an office worker at a desk:

Like every analogy it falls apart if you push it too hard (don't take it to court), but it nails the core idea which is that you do** your work at the desk, you keep your things in the cabinet, and those are not the same piece of furniture.**
The whole machine, in one breath: the CPU does the work (it calculates), the memory (RAM) holds whatever it's working on right now (and forgets it the moment the power is gone), and the storage keeps everything permanently. Input brings things in, output sends things out. That, genuinely, is a computer. Everything else from here is just detail.
Two quick things to plant before we move on. One: that little chip on our AirBox (The ESP32)? Same story, same basic parts. It is a computer too, just a tiny one. Two: there is more than one way to wire the CPU and the memory together, and (plot twist) your laptop and that AirBox chip actually do it differently. That difference is exactly what the next section is about.
Well nobody. Sorta. Let me explain.
The way those components are built today, the way they interact, the medium in which they are put, all of those are consequences of a theoretical model.
You see, computer science is really a science. With the whole scientific method and theory part. At some point some really smart people (including prolific mathematician, physics genius, computer science master and known party animal John von Neumann and the even more well known but less crazy-minded Alan Turing, on whom I wrote more in the history section of CS1) set out to find theoretically efficient ways in which we could trick different devices into doing the abascus work for us. Using math, logic and possibly a lot of cocaine they arrived at some really efficient models for computation, they're called ARCHITECTURES.
They're like blueprints on how to design computing devices.
Let's put a better definition forward:
Here's the key idea, and it's bigger than computers. An architecture is a model: a simplified plan that describes how something works without drowning you in every microscopic detail.
Think about it. A real CPU is a hellish nightmare of billions of microscopic transistors. Nobody, not even the engineers who design them, thinks about all billion at once. It would be like trying to understand a city by tracking every single person in it simultaneously. Your brain would melt (perhaps a better fate than rotting?). So instead we zoom out to a level where things make sense: "the CPU fetches an instruction from memory, does it, grabs the next one." That sentence ignores a staggering amount of detail, intentionally skipping past the continuous ons and offs those untold billions of transistors do at once!, and that's exactly the point. A good model throws away the details that don't matter so you can actually reason about the things that do.
Here you can see both a bird's eye-view of a CPU (Intel Pentium III) and a closeup of a silicon die.
.png)

What a model is, and what it is NOT. A model is a useful simplification, not the literal truth. The architecture we're about to draw is NOT a photograph of what's physically etched into the silicon, real chips are messier and full of clever shortcuts. It's a mental picture that's close enough to reality to let you predict and understand how the machine behaves. Engineers, scientists, and you in this course all do the exact same trick: work with the simplest model that still gives the right answers. Same move as the office-worker-at-a-desk from last section.
So why do architectures matter, concretely? Two reasons:
With that out of the way, let's meet the model basically every computer descends from.
Back in the 1940s, a plan was written down for how to organize a general-purpose computer. It's named after John von Neumann (I'll stop glazing him; fun fact: he also invented game theory and cellular automata...he was onto that "game of life" stuff way before Conways), and almost every computer you've ever touched, your laptop, your phone, is built on it.
The model is beautifully simple. There's a CPU (the doer) and there's a single memory, connected by a path between them (the road that carries stuff back and forth is called a bus). And here's the one idea that makes von Neumann von Neumann:
the program and the data live together in that same one memory.

It's sneakier than it looks. The instructions (your program, the recipe of steps) and the data (the numbers the program is chewing on) are stored in the same place, side by side, in the same memory. To the memory itself, they're all just numbers in boxes. It doesn't "know" which boxes are recipe and which are ingredients.
The CPU then runs in a relentless little cycle (you may have seen this in the history section as fetch-decode-execute; if not, maybe take a look ):
[DIAGRAM: the fetch-decode-execute cycle as a simple loop, three boxes "FETCH -> DECODE -> EXECUTE" arranged in a circle with an arrow looping back from EXECUTE to FETCH. Caption: "billions of times per second".]

Why was "keep the program in memory too" such a massive deal? Because it means a computer can run any program just by loading different instructions into memory, no rewiring, no rebuilding. The same machine becomes a calculator, then a text editor, then a game, just by putting different numbers in the boxes. That single idea, the program is just data you can load, is the reason one device in your pocket can do ten thousand different jobs. It's the whole game. In the history page I explained a little how, at some point, for every program, the computer has to be PHYSICALLY REWIRED, as in having humans plug and unplug cables into pegboards in the back of the computers which took WEEEEEKS. A bloody eternity.
But living together has a price, and it's worth seeing:
The catch is... since instructions and data share one memory and one bus, the CPU can't grab an instruction AND grab data in the very same moment, they have to take turns on that single road. Picture a one-lane bridge that both recipe and ingredients must cross: only one thing crosses at a time. This traffic jam even has a nickname, the "von Neumann bottleneck." It's the price you pay for the flexibility. (There are clever tricks to ease it, but that's a story for another day.). REad more about his limitation here. It even affect AI, more here.
Now the payoff. Your laptop is von Neumann. But the little chip on our AirBox is built on a different plan, called modified Harvard, and once you see why, the whole "architectures matter" point clicks.
The difference is exactly the thing we just made a fuss about. Where von Neumann keeps program and data together, a Harvard-style design keeps them apart:

Two separate memories, often two separate roads. ("Modified" Harvard just means real chips blend the two ideas a bit rather than being religiously pure, remember, models are simplifications and reality is messier.)
So what does that buy you, in plain consequences you can actually feel?
The contrast in one breath: your laptop (von Neumann) keeps program and data mixed in one memory, which makes it endlessly flexible, load any program you want, but they have to share one road. The AirBox chip (modified Harvard) keeps program and data in separate memories, which makes it boot instantly, stay reliable, and avoid the traffic jam, at the cost of being less of a do-anything machine. Neither is "better." They're different tools for different jobs, and that is why the architecture you choose actually matters.
The big thing to carry forward: from here on in this course, when we write C, we're writing for a von Neumann machine, one memory, holding both our program and our data, together.
The code that goes into the AirBox is another story completely.
So far we've talked about the machine. Let's switch gears and talk about an idea, one that has nothing to do with wires or chips or even computers, really. It's about something much more basic: how you arrange your stuff.
We'll be working with memory. Lots of it. You can't even fathom how big a Gb or even a Mb is in reality. We're talking about billions of bytes of data. Working with and storing this data efficiently dictates how good our computer does the computing stuff.
The architecture of how to represent this data or rather the model of how it should be represented is called DATA STRUCTURE.
a data structure is just an agreed-upon way of organizing a collection of things.
That's it. Forget computers for a second. You already use data structures every single day, you just don't call them that:
Notice none of those examples mention a computer. A data structure is an idea about arrangement, not a physical thing. The same way a "triangle" in maths isn't any particular triangle drawn on paper, it's the concept of three corners and three sides, a data structure is the concept of how things are arranged and what rules they follow. It's a mental model.
To illustrate a space where data goes, we often use boxes in computer science. Just know we're not talking about a physical space with volume where you put stuff, it's just a way of expressing "empty space where information can go". But it's neat to think about them as boxes waiting for something to fill them up.
The key idea, and it's a mathematician's way of thinking: a data structure is defined by the rules for putting things in and taking things out, not by what it's made of. Just like in maths you can describe what a "set" or a "sequence" is purely by its rules, without drawing it, a data structure is described by its rules. "What can I do to it, and what happens when I do?" Answer that, and you've defined the structure. We're staying at the level of the IDEA here, never mind how a computer would actually store it. That comes much later.
So why do we care? Because the arrangement you pick decides what's easy and what's hard. Keep your contacts in a random jumble and finding "Maria" means checking every single one. Keep them alphabetical and you find her instantly. Same data, different structure, wildly different experience. Choosing the right data structure for the job is a huge part of what it means to think like a computer scientist, and you'll meet a whole zoo of them over time (lists, queues, trees, and more).
But we're going to zoom in on one, because it's simple, it's everywhere, and, this is the sneaky part, it's secretly going to explain something important a few sections from now.
A stack is about the simplest data structure there is. The rule is one sentence:
you can only add to the top, and you can only take from the top.
That's the whole structure. Think of a stack of plates in a cafeteria. You put a clean plate on top. The next person takes the top plate. You never, ever yank a plate from the middle of the stack, that's madness, the whole thing topples. New stuff goes on top, stuff comes off the top.

This "top only" rule has a name you should know, because it describes the behavior perfectly: LIFO, which stands for Last In, First Out. The last thing you put on is the first thing you take off. The plate you set down most recently is the one the next person grabs. The plate at the very bottom? It's been there the longest, and it won't come off until everything above it is gone.

There are only really two things you ever do to a stack, and they have proper names:
And that's genuinely it. No "grab the third one down," no "take from the bottom." Push on top, pop from the top. The simplicity is the feature.
Why on earth are we fussing over a stack of plates? Because, plot twist for later: when your programs start calling one piece of work from inside another, the computer keeps track of it all using exactly this structure, a stack. The "last one started is the first one finished" rule turns out to be precisely how a running program juggles its tasks. You don't have the full picture yet, and that's fine. Just lock in the idea, a stack is last-in-first-out, push on top, pop from the top, and a few sections from now, when we get to functions, it's going to click into place with a very satisfying snap.
For now, that's all you need. A data structure is a way of arranging things, defined by its rules, not its materials. There are many. The one to remember is the stack: add to the top, take from the top, last in, first out. Hold that thought.
Alright. Theory hour is over (for now =)). You know what a computer is, you know roughly how it's wired, you even know what a stack is. But so far you've been a spectator. Time to grab the wheel and make this machine do something. To do that, you have to talk to it. And to talk to it, you need a language.
A programming language is a human-friendly way of writing instructions for a computer. That's the whole definition. Instead of you having to think like a machine, the language lets you write something a person can read and understand, and then it gets turned into something the machine can run.
Why do we need a middleman language at all? Cast your mind back to section 1: the CPU is gloriously dumb, and it only understands a tiny set of baby steps, written as raw numbers (this is called machine code, and it's exactly as fun to read as it sounds). You could write programs as naked numbers directly. People did, once. It was agony. So instead we write in a language like C, which looks vaguely like math and English had a baby, and then we let a tool translate it down into those numbers for us. Best of both worlds: you write something readable, the machine gets something runnable.
The language we'll use is C. It's old (born in the early 1970s, written by Dennis Ritchie at Bell Labs), and yet it is everywhere: the guts of Windows, macOS, and Linux are C, a huge slice of the programs on your phone, and, the part that matters to us, the little chip on our AirBox runs code written in C. When you learn C, you're learning the language a stunning amount of the modern world is actually built on.
But here's the real reason we teach C, specifically, in a course about understanding computers:
C is close to the machine. Most modern languages wrap the computer in thick bubble wrap, they hide the memory, hide the hardware, hide what's really going on, so you can build things fast without worrying about the details. That's great for getting work done. It's terrible for learning how computers actually work. C does the opposite: it hands you the machine almost directly, memory and all. It hides very little. That makes C harder, but it makes it the perfect language to actually SEE what a computer is doing. By the end of this, you won't just be writing programs, you'll understand what they're doing to the machine underneath.
A fair warning: C is powerful, and that power cuts both ways. C trusts you completely. It will let you make mistakes that other languages would slap your hand for, and (remember the golden rule from section 1) the machine will then do exactly what you wrote, mistake and all, without complaint. For learning, this is a feature, not a bug: you'll feel the consequences directly, and that's how you actually understand the machine. Just go in knowing C does not hold your hand.
Want the origin story? Where C came from (Bell Labs, the Unix operating system, the whole legendary tale, plus a genuine note from co-creator Brian Kernighan) is a great read, and it lives in the "Why C?" side-read in the history section. Optional, but worth it.
Here's the catch. You can read C. The CPU cannot. Remember, the CPU only eats machine code, those raw numbers. Your beautiful, readable C file means absolutely nothing to it.
Myth: "I write C, and the computer runs my C."
WRONG. The CPU never sees your C at all. It only ever runs machine code (numbers). Your C has to be translated into those numbers first, before the machine can run a single line of it. So who does the translating? That's the next character in our story.
A compiler is a program whose entire job is to translate your C code into machine code the CPU can actually run. You write C, you hand it to the compiler, and out the other end comes a runnable program (an executable).
The shape of it is simple:

The text file you write your C in is called source code (it usually ends in .c). The compiler reads that file, checks it over, and translates it.
Translate once, run many times. Here's something important about how C works: the compiler does its translation once, BEFORE the program ever runs. That moment of translation is called compile time. After that, you've got your finished executable, and you can run it as many times as you like (that's run time) without translating again. Compile once, run forever. (Some other languages translate on the fly every time they run, C doesn't, and that's a big part of why C programs are so fast.)
And the compiler isn't just a translator, it's also your first line of defense. If your C has mistakes it can catch, broken grammar, a misspelled keyword, a missing piece, the compiler refuses to translate and tells you what's wrong. This is called a compile error, and (counterintuitively) it's the compiler being your friend: it caught the mistake before your program ever ran. Get used to compile errors. You'll see a lot of them. Everyone does, forever.
I said "the compiler translates your code," like it's one step. It's actually more like a little assembly line, a few stages, each handing its work to the next. You don't have to memorize this, but knowing the stages exist is genuinely useful, because when something breaks, the error usually tells you which stage complained.
Here's the line your code goes down:

Walking the stages once, in plain language:
# (you'll see one in a second). Mostly it's a glorified copy-paste step that pulls in extra code you asked for, before the real translation begins.Relax, you trigger all of this with ONE button. That looks like a lot of stages, but you never run them by hand. One command (or one "Run" button in the tool we'll use) sets the whole assembly line in motion automatically, and you just get your program at the end. The stages matter only so that when an error says "linker error" instead of "compile error," you'll have a clue where to look.
Want to SEE C become assembly with your own eyes? There's a wonderful free tool called Compiler Explorer (godbolt.org) where you type C on the left and watch the assembly appear on the right, instantly. Hover a line of C and it highlights the exact machine steps it became. It's the best way to feel what "translation" really means. Have a play once you've written a program or two.
There's a tradition, older than most of you, that the very first program you write in any language just makes the computer say "Hello, world!" on the screen. It's a rite of passage. So let's do it. Here is a complete, real C program:
#include <stdio.h>
int main(void) {
printf("Hello, world!\n");
return 0;
}
That's it. That's a whole program. Let's not over-explain it (you'll meet every piece properly in the coming sections), but here's enough so it isn't pure magic:
#include <stdio.h> is one of those # lines for the preprocessor. It pulls in a toolbox (called stdio, for "standard input/output") that contains, among other things, the tool for printing to the screen. Without it, the program wouldn't know how to print.int main(void) marks where the program starts. Every C program begins running at a spot called main. Think of it as the front door. The { and } wrap up everything main does, that pair of curly braces is the body of the program.printf("Hello, world!\n"); is the line that does the actual work: it prints the text between the quotes. (That weird \n on the end means "new line", it just moves the cursor down afterwards. More on that later.)return 0; is main reporting back to the computer: "I finished, everything went fine." (0 means "all good".);) at the ends of those lines. In C, a semicolon marks the end of an instruction, like a full stop at the end of a sentence. Forget one and the compiler will complain (see? friend.)You don't need to install anything to write C. We'll use an online editor (such as OnlineGDB) where you type your code, hit a button, and it compiles and runs right there in your browser. No setup, no headaches.
And this is the loop you'll live in from now on, the actual heartbeat of programming:
Write a bit of code. Compile it. Run it. Look at what happened. Did it work? Did it error? Fix it, and go round again. You will go around this loop more times than you can count, and that is completely normal, that loop is programming.
Myth: "If my program compiles, it's correct."
WRONG, and this one bites people for years. Compiling successfully only means your grammar is fine, the compiler understood what you wrote. It says NOTHING about whether your program does the right thing. You can write a perfectly grammatical program that does something completely wrong, and it'll compile happily and then misbehave (golden rule again: it does what you SAID, not what you MEANT). The compiler catches broken grammar. It cannot catch a bad idea. That part's on you.
The big picture, in one breath: C is a human-readable language for writing instructions. The CPU can't read it, so a compiler translates your C source code into the machine code the CPU runs, doing it once (at compile time) through a little pipeline (preprocess, compile, assemble, link) that you kick off with a single button. You write, compile, run, and fix in a loop. That loop is the job. Welcome to programming.
Next up, we finally start filling that program with something interesting: actual data. Numbers, letters, values to juggle, and the place the machine keeps them all while it works. Which means it's time to properly meet memory.
mainEvery C program needs a single, agreed place where execution begins, and in C that place is always main. We call it the program's entry point: the one spot the machine jumps to first when your program starts running. It doesn't matter how much code you write around it, the computer ignores all of that until it finds main, and that's where the very first instruction is taken from. Think of it as the front door: there's exactly one, and everyone comes in through it.
For now, main is the whole story, your program starts there and runs straight down. But "where does execution go, and how does it find its way back?" turns out to be a much richer question the moment your programs start handing work off to other chunks of code. When we reach functions (very soon), you'll see that the machine keeps track of where it is, and where to return to, using a stack, exactly the last-in-first-out structure from section 3. That's the satisfying snap I promised: main is just the first thing on that stack, the bottom plate in the pile. Hold the thought; it pays off shortly.
So far your variables just sit there in their little boxes. Useful, but static. This section is where the program comes alive: you'll compute with your values (operators), talk with the user (input and output), and, the big one, let the program make decisions and repeat itself (control flow). That last part is what turns a fixed list of steps into something that actually reacts. This is where your pseudocode plans become real, running C.
An operator is just a symbol that does something to your values. You already know most of them from maths class; C only changes a couple of symbols.
Arithmetic operators do the obvious number-crunching:
int a = 10, b = 3;
int sum = a + b; // 13 (add)
int diff = a - b; // 7 (subtract)
int prod = a * b; // 30 (multiply, note: * not ×)
int quot = a / b; // 3 (divide... wait, not 3.33?)
int rem = a % b; // 1 (the REMAINDER, a brand-new one)
Two of those deserve a closer look. First, /: remember the integer-division trap from section 5? 10 / 3 is 3, not 3.33, because whole-number boxes can't hold the fraction, so it gets chopped. (Same rule, it never stops mattering.) Second, that % is the modulo operator, and it's genuinely new: it gives you the remainder of a division. 10 % 3 is 1, because 10 divided by 3 is 3 with 1 left over. It looks niche, but it's secretly everywhere, the classic use: number % 2 is 0 for even numbers and 1 for odd ones. Keep % in your back pocket; you'll reach for it constantly.
Comparison operators don't compute a value, they ask a yes/no question about two values, and answer with true or false:
a == b // is a EQUAL to b? (note: TWO equals signs!)
a != b // is a NOT equal to b?
a > b // is a greater than b?
a < b // is a less than b?
a >= b // greater than or equal?
a <= b // less than or equal?
The single most common beginner bug in all of C:
=vs==. One equals sign (=) assigns a value ("put 5 into x"). Two equals signs (==) compares ("is x equal to 5?"). They are completely different, and mixing them up is a rite of passage that bites everyone.x = 5changes x.x == 5asks a question about x. Burn this in now: one=does, two==asks.
About "true and false", a quick but important note, because C is old and a little quirky here. C has no real "true/false" built in (well, not originally). Instead it uses numbers: 0 means false, and anything that isn't 0 means true. A comparison like a > b actually hands back 1 for true or 0 for false. You usually won't notice, but it explains some C weirdness down the line, and it's a reminder of the golden thread: it's all just numbers.
Logical operators let you combine yes/no questions, exactly like in plain English:
(age >= 13) && (age <= 19) // AND: true only if BOTH are true (a teenager)
(day == 6) || (day == 7) // OR: true if EITHER is true (it's the weekend)
!(logged_in) // NOT: flips it (true becomes false)
&& is and (everything must be true), || is or (at least one must be true), and ! is not (it flips true and false). These are how you ask richer questions than a single comparison can.
Watch your precedence (order of operations). Just like maths does
×before+, C does operators in a set order: it multiplies and divides before it adds and subtracts, and it does arithmetic before comparisons. So2 + 3 * 4is14, not 20. When in doubt, use parentheses to force the order you mean,(2 + 3) * 4. Parentheses cost nothing and they make your intent obvious to both the compiler and the next human (often future-you). When a calculation misbehaves, precedence is a prime suspect.
You already met printf for output (printing to the screen) in the hello-world program. Let's actually use it, because to print a variable you need one more trick: a placeholder.
int score = 95;
printf("Your score is %d\n", score); // prints: Your score is 95
That %d is a format specifier, a little placeholder that means "stick a whole number here," and score is the value that gets slotted in. The type of the value decides which placeholder you use, and there are only a few to remember:
%d, for an int (a whole number)%f, for a float or double (a decimal)%c, for a char (a single character)\n, which isn't a placeholder at all, it just means "newline" (move to the next line)int age = 16;
float price = 4.99;
char grade = 'A';
printf("Age: %d, price: %f, grade: %c\n", age, price, grade);
The placeholders get filled in order, left to right, by the values listed after the text. Simple as that.
Match the placeholder to the type, or you get garbage. Use
%dfor anint,%ffor a decimal,%cfor a character. Mismatch them, say, print a decimal with%d, and C won't necessarily stop you; it'll just print nonsense (golden rule: it does what you wrote). If your output looks insane, check that your placeholders match your value types.
Now the other direction: input, getting a value from the user with scanf. This one has a famous gotcha, so look closely:
int age;
printf("How old are you? ");
scanf("%d", &age); // reads a whole number, stores it in age
printf("Next year you'll be %d\n", age + 1);
scanf uses the same %d-style placeholders to know what kind of value to read. But notice that & in front of age. That ampersand means "the address of", you're not handing scanf the value in the box (there's nothing useful in it yet), you're handing it the box's location so it knows where to put the value it reads. (Remember from section 5: every box has an address.)
Don't forget the
&inscanf. This is one of the most common beginner mistakes. To print a variable you giveprintfthe variable itself; to read into a variable you givescanfthe address of it, with&. Forgetting the&is a classic crash-or-garbage bug. For now, just learn it as a rule:scanfneeds the&. Why it works this way is genuinely fascinating, and it's the doorway to one of the most powerful ideas in C (addresses and pointers), but that's a story for a later course. Today:scanf, ampersand, done.
Here's the big one. Until now, your program runs straight down, top to bottom, every line once, in order. That's it. But real programs need to choose ("if the score is high enough, show the win screen") and repeat ("ask again until they type a valid answer"). That power, controlling which lines run and how many times, is called control flow, and it's what makes a program more than a fancy calculator.
Making decisions: if / else. This is the most important construct in programming. It runs a block of code only if a condition is true:
int score = 75;
if (score >= 50) {
printf("You passed!\n");
} else {
printf("You failed. Try again.\n");
}
Read it like English: if the thing in the parentheses is true, run the first block (between its { }); otherwise (else), run the second block. The condition is just one of those yes/no questions from the comparison operators. You can also chain them for more than two cases with else if:
if (score >= 90) {
printf("Grade: A\n");
} else if (score >= 75) {
printf("Grade: B\n");
} else if (score >= 50) {
printf("Grade: C\n");
} else {
printf("Grade: F\n");
}
C checks each condition top to bottom and runs the first block whose condition is true, then skips the rest.
Decisions live or die on the condition, and
=vs==strikes again. Inside anif, you almost always want==(compare), not=(assign). Writingif (score = 50)doesn't check whether score is 50, it sets score to 50 and then, because that's non-zero, treats it as always true. A nasty, silent bug. When anifalways runs (or never does), suspect the condition first.
Repeating with a condition: the while loop. A while loop runs a block over and over, as long as its condition stays true:
int count = 1;
while (count <= 5) {
printf("%d\n", count); // prints 1, 2, 3, 4, 5
count = count + 1; // <-- crucial!
}
It checks the condition; if true, it runs the block, then checks again; and it keeps looping until the condition finally becomes false. This is a direct echo of the pseudocode "repeat until..." plans, now in real C.
The infinite loop: the trap that freezes your program. Look hard at that
count = count + 1line. If you forget it,countstays1forever, the conditioncount <= 5is always true, and the loop never ends, your program just hangs there printing 1 forever until you kill it. This is an infinite loop, and everyone writes one eventually. The rule: inside everywhile, something must change in a way that will eventually make the condition false. If a loop won't stop, that's the first thing to check.
Repeating a set number of times: the for loop. Counting loops are so common that C has a tidier shape for them, the for loop. It does the exact same job as the while above, but bundles the three pieces (start, condition, change) onto one line so you can't forget any of them:
for (int i = 1; i <= 5; i = i + 1) {
printf("%d\n", i); // prints 1, 2, 3, 4, 5
}
Read the parentheses as three parts separated by semicolons: start (int i = 1, set up a counter), condition (i <= 5, keep going while this is true), and change (i = i + 1, do this after each pass). It's the go-to loop whenever you know how many times you want to repeat. (And keeping the counter-update right there in the header is exactly why for loops trip people into infinite loops far less often than while.)
A couple more you'll meet. There's also a
switchstatement, a tidy alternative to a longelse ifchain when you're checking one variable against many fixed values, and the keywordsbreak(jump out of a loop early) andcontinue(skip to the next pass). You don't need them yet; just know they exist so they're not a surprise when you see them. Theif,while, andforabove are the bread and butter, get comfortable with those three and you can write almost anything.
Watch all three ideas (operators, I/O, control flow) cooperate in one tiny but complete program, that classic "guess my number" game, which is also, sneakily, the binary-search idea from before, made playable:
#include <stdio.h>
int main(void) {
int secret = 42;
int guess;
printf("Guess my number (1-100): ");
scanf("%d", &guess);
while (guess != secret) {
if (guess < secret) {
printf("Too low! Try again: ");
} else {
printf("Too high! Try again: ");
}
scanf("%d", &guess);
}
printf("You got it! The number was %d.\n", secret);
return 0;
}
Trace it once in your head: it reads a guess (input), then loops as long as the guess is wrong (while), each time deciding whether to say too high or too low (if/else) and reading again, and finally prints the win when the loop ends. Operators, I/O, and control flow, all earning their keep in eight lines. That's a real program.
See your program's insides as it runs. Tracing code in your head is a skill, but a tool can do it for you while you learn. Paste a program into Python Tutor (pythontutor.com/c.html), and it'll step through your C one line at a time, showing you exactly which branch the
iftakes and watching your loop counter tick upward in memory. For getting a real feel for how control flow actually moves through your code, it's hard to beat. Run that guessing game through it and watch the loop go around.
The big idea, in one breath: operators compute (
+ - * /, the remainder%) and ask yes/no questions (==,<,&&, ...),printf/scanfsend values out and read them in (mind the placeholders, and the&on scanf), and control flow is what makes a program think:if/elsechooses which code runs, whilewhileandforrepeat code until a condition says stop. Decisions and loops are the leap from "a fixed list of steps" to "a program that reacts." Watch out for the two classic traps along the way:=vs==, and the infinite loop.
You can now write genuinely useful programs. But you may have noticed something: as programs grow, you end up repeating chunks of logic, and main gets long and messy. The fix is to package reusable pieces of code and give them names, and that's the next section: functions, where we'll finally cash in that promise about the stack from section 3.