Why C, and What a Program Really Is
The Story of C: From a Back Room at Bell Labs to Your Phone
In this lesson
- Place C on a timeline that runs from ALGOL 60 to Rust.
- Explain why the Unix team needed a new language in 1972.
- Name three languages that borrowed C's syntax, and say what they borrowed.
It is 1969, in a back room at Bell Labs in New Jersey. A small team has got hold of a PDP-7, a minicomputer nobody else wanted. They are building an operating system on it. They call it Unix. It works.
It is also chained to that one machine. It is written in assembly language, and assembly differs on every processor. Move to a new computer and you start again from nothing.
Languages are shaped by the problem their creators were stuck on. That was the problem. C is the answer, and almost every odd corner of C makes sense once you know it.
The assembly problem
Assembly language is an almost word-for-word text version of the instructions a processor understands. It is fast and exact. It is also tied to one family of chips.
So an operating system written in assembly cannot move. Every new machine means rewriting tens of thousands of lines, by hand, correctly. Bell Labs wanted a language close enough to the hardware to write a kernel. It also had to be general enough to move Unix to a new machine.
No language at the time did both. The high-level languages of the day hid the machine. Assembly did not hide it, and did not travel.
From BCPL to B to C
The chain starts in Cambridge. Martin Richards wrote BCPL in 1967, by cutting down an earlier language called CPL. CPL had itself grown out of ALGOL 60. Ken Thompson then cut BCPL down further into a language he called B, around 1969.
B had one serious limitation. Every piece of data was a plain machine word. B could not say "this is one character, that is a whole number". It had no way to describe a record with three parts.
Dennis Ritchie fixed that at Bell Labs around 1972. He added a type system, which is a language knowing what kind of thing each piece of data is. He kept everything else small. The result was C, so named because it came after B. Programmers are not always poets.
1973: Unix is rewritten in C
This is the moment that mattered. In 1973 the Unix kernel itself was rewritten in C. An operating system is the software tied most tightly to the hardware. It was now written in a language that could move between machines.
That had not been done at this scale before, and it settled the argument. Within a decade, C was how systems got written. Every operating system you will use this year still carries that decision.
Think of it this way
Ritchie made three choices that explain most of C's personality. Trust the programmer: C lets you do dangerous things, and assumes you meant to. Keep the language small and the library separate: C itself does not even know how to print. That is why it fits on a smart card with 2 KB of memory.
Do not hide the machine: a char is one byte and a pointer is an address. Nearly every line you write maps to something real.
The standards, and what each one gave you
C was not frozen in 1972. It was standardised, and then revised roughly every decade. You will meet the results of every row below in this track.
| Year | Name | What it added that you will actually meet |
|---|---|---|
| 1978 | K&R C | The book by Kernighan and Ritchie. No standard yet, so the book was the reference. Functions were written with the parameter types on separate lines below the name. |
| 1989 | ANSI C, then ISO C90 in 1990 | The first official standard. Function prototypes (int add(int a, int b);), the void keyword, and a defined standard library. |
| 1999 | C99 | The // comment. Declaring a variable in the middle of a block, and inside a for. long long. bool through <stdbool.h>. |
| 2011 | C11 | Threads, better Unicode support, and _Static_assert, a check the compiler runs while it builds. |
| 2018 | C17 | Bug fixes and clearer wording, no new features. This is the standard this track compiles with. |
| 2024 | C23 | nullptr, constexpr, and bool, true and false as real keywords. Old-style function definitions were removed. |
The next four programs are the same idea written in four eras. All four still compile today. Read them as a timeline, not as code to memorise.
Notice where the parameter types sit. They are on their own lines, below the function name.
#include <stdio.h>
/* K&R style: the parameter types come after the name. */
int add(a, b)
int a;
int b;
{
return a + b;
}
int main()
{
printf("%d\n", add(2, 3));
return 0;
}
5
This still builds under C17. Ask for the newest standard and the compiler complains: warning: old-style function definition [-Wold-style-definition]. C23 removed the form. Fifty year old code is still being retired, slowly.
Two changes arrive here. A prototype announces the function before it is used. And every variable is declared at the top of its block, before any other statement.
#include <stdio.h>
/* C89: a prototype, and every variable declared at the top of the block. */
int add(int a, int b);
int main(void)
{
int i;
int total;
total = 0;
for (i = 1; i <= 5; i++) {
total = total + i;
}
printf("%d\n", add(total, 0));
return 0;
}
int add(int a, int b)
{
return a + b;
}
15
The prototype is the real gain. It lets the compiler check that you called add with two whole numbers. Before prototypes, a wrong call quietly produced nonsense.
This is the version that looks modern, and it is twenty-six years old.
#include <stdbool.h>
#include <stdio.h>
int main(void)
{
// C99 added this comment style.
long long big = 9000000000LL;
bool ready = true;
int total = 0; // declare where you need it
for (int i = 1; i <= 5; i++) { // and inside the for
total += i;
}
printf("%d %lld %d\n", total, big, ready);
return 0;
}
15 9000000000 1
Four of C99's additions sit in these lines. They are the // comment, long long for numbers too big for int, bool from a header, and declaring i inside the for. You will write all four from Module 2 onward.
C11 added a way to make the compiler refuse to build, on purpose.
#include <stdio.h>
/* C11 added a check the compiler runs, not the program. */
_Static_assert(sizeof(int) >= 4, "this code needs int to be at least 4 bytes");
int main(void)
{
printf("int is %zu bytes here\n", sizeof(int));
return 0;
}
int is 4 bytes here
Move this program to a machine where int is two bytes and it will not build. The message says why. That is kinder than finding out at run time.
C's children
C did not only survive. It set the shape that most popular languages still use.
Three of them are worth naming precisely, because "borrowed from C" is vague.
- C++ (1985) borrowed almost everything, on purpose. It started as "C with Classes" and kept C's types, operators, pointers and standard library. Most valid C is still valid C++, which is why this track leads straight into the C++ one.
- Java (1995) borrowed the syntax and threw away the pointers. Braces, semicolons,
if,while,for, and the same operators all came across. Manual memory management did not. - JavaScript (1995) borrowed only the look. The braces, the semicolons and the
forloop are C's. Everything underneath, including its type rules, came from elsewhere. The name was a marketing decision, not a family tie to Java.
Where this is used
- Linux, born 1991. Linus Torvalds started it as a student project and still leads it. It is written in C. Ritchie's 1972 portability goal is why the same kernel runs on a phone, a laptop and a supercomputer.
- SQLite, born 2000. D. Richard Hipp wrote it in C for a US Navy project. The goal was to remove the need for a database server on a ship. It is now in your browser and your phone, and it is public domain.
- Git, born 2005. Written in C by Torvalds in about two weeks, after the Linux project lost access to its previous tool. Every commit you will ever make runs through that C code.
Common mistakes
1. Thinking "old" means "obsolete".
C is older than most of the people using it. It has also stayed in the top five of the TIOBE index since that index began in 2001. The newest compiler answers fifty year old code with a warning, not a refusal. Code that still builds after fifty years is evidence of care, not decay.
2. Copying a K&R function definition from an old book or an old answer.
int add(a, b)
int a;
int b;
{ return a + b; }
Compile this with gcc -std=c23 and you get warning: old-style function definition [-Wold-style-definition]. The fix is the C89 form: int add(int a, int b). Write the types in the brackets and you will never meet this again.
3. Assuming C and C++ are the same language.
They share ancestry, not rules. A C file compiled by a C++ compiler can fail on things C allows, such as an implicit conversion from void *. When an error message mentions g++ and your file ends in .c, that is the first thing to check.
4. Believing JavaScript is a version of Java.
It is not, and this confuses interviewers less than it confuses learners. They share braces and semicolons because both copied C. Everything else differs, including how they handle numbers and memory.
Draw the ancestor chain from memory, on paper: five boxes with names and years, oldest at the top.
Rules. No looking at Figure 1. Beside each arrow, write one short phrase saying what the newer language changed.
Check yourself. Compare with Figure 1. Getting a year wrong by a year or two is fine. Getting the order wrong is worth a second read of this lesson.
Take Example 1, the K&R version, and rewrite it in the C89 style by hand, on paper. Then type it into the Playground and run it.
Rules. Move the parameter types into the brackets. Add a prototype above main. Change int main() to int main(void). Output must stay 5.
Check yourself. Your version should look like Example 2's add function. If the program prints anything other than 5, you changed more than the shape.
Ritchie chose "trust the programmer" over "stop the programmer". Argue both sides in writing, using real evidence rather than opinion.
Rules. Two paragraphs, roughly five sentences each. One paragraph must name a real category of bug that this choice allows. The other must name a real system that could not exist without it.
Check yourself. A good answer mentions memory safety on one side and kernels or embedded devices on the other. Keep it. You will revisit this in Module 16, with C23 and Rust in hand.
Common doubts
Do I have to memorise these dates?
No. Two facts are worth keeping: C is from 1972, and the version you compile with is C17 from 2018. The rest is context that makes error messages and old code readable.
Which standard should I write in?
C17, which is what this track and the Playground use. Write C99 style code and it will build everywhere, including on old university machines. Avoid C23 features until you know the marking machine accepts them.
If C23 exists, why is this track on C17?
Because C17 is what compilers, universities and judges agree on today. C23 support is still uneven across toolchains. Module 16 covers what changed, so you will not be surprised by a C23 codebase.
Is C++ just a newer C?
No. It is a different language that started from C and grew for forty years. Most simple C programs compile as C++, which makes the move easy. The idioms, the standard library and the style are all different.
Will Rust replace C?
Not soon, and not everywhere. Rust is being used for new systems code, including parts of the Linux kernel since 2022. Thirty million lines of working C do not get rewritten because a better language exists.
Key takeaways
- C exists because Unix could not move between machines while it was written in assembly.
- The chain is ALGOL 60, CPL, BCPL, B, then C in 1972, with types as the big addition.
- Rewriting Unix in C in 1973 proved a portable language could do systems work.
- The standards are K&R, C89, C99, C11, C17 and C23; this track compiles C17.
- C++ borrowed the language, Java borrowed the syntax, JavaScript borrowed the look.
- Ritchie's three choices were trust the programmer, keep it small, do not hide the machine.
Next you will follow one small file from text to a running program. There are four separate stages, and nobody usually shows you any of them.
Keyboard: পরের lesson এ j, আগেরটায় k, editor খুলতে r।