Before learning variables, loops, pointers or functions, it is worth understanding why C was created in the first place.
Today, C is often introduced as a programming language that teaches the basics of programming. That description is useful for a beginner, but historically it misses the much more interesting story. C was not created as a classroom language, nor was it designed simply to give programmers another way to write applications.
C emerged from a much more practical problem at Bell Labs in the early 1970s: how could programmers build and maintain an operating system without writing almost everything in assembly language?
The answer gradually became C.
What makes the story even more interesting is that C did not appear suddenly. It evolved alongside the early development of Unix, passing through several stages and earlier languages before becoming the language we recognize today. Dennis Ritchie later described C as coming into existence between 1969 and 1973, with 1972 being the most creative period in its development.
To understand C properly, we need to go back before C existed.
Before C: The Problem With Building Systems in Assembly
In the late 1960s, computer systems were very different from what we use today. Memory was expensive, machines were small by modern standards, and programmers had to work much closer to the hardware.
At Bell Telephone Laboratories, researchers had been involved in the Multics project, a large and ambitious operating-system project. By 1969, Bell Labs had decided to leave Multics because the project was becoming too expensive and its promised results seemed too far away. Around the same time, researchers including Ken Thompson began exploring the possibility of building a smaller and more manageable computing environment of their own.
That effort eventually produced Unix.
The first Unix system was developed on a DEC PDP-7, a machine with extremely limited resources compared with modern computers. Ritchie later recalled that the early PDP-7 Unix environment had only about 8K 18-bit words of memory available. The original Unix system was written in PDP-7 assembly language.
Assembly language gave programmers the control they needed, but it also created a major problem: the operating system became tightly connected to one particular machine and extremely difficult to develop and modify.
Writing a small program in assembly is one thing. Writing an entire operating system, its utilities and supporting tools in assembly is something very different.
The researchers wanted a better way.
They needed something more expressive than assembly, but still efficient enough to work close to the machine.
That search eventually led through BCPL, B, NB and finally C.
From BCPL to B
One of the important ancestors of C was BCPL, a language created by Martin Richards in the 1960s.
BCPL influenced the design of B, which was created by Ken Thompson around 1969–70. Ritchie later described BCPL as the “grandparent” of C and B as its immediate parent.
Thompson wanted a language that could help him build software for the new Unix environment without requiring everything to be written in assembly.
The result was B.
B was deliberately small and simple. That mattered because the machines on which it was being developed had severe memory limitations. Thompson had to keep fighting the size of the compiler itself; adding features could make the compiler too large to fit comfortably, while rewriting it using newly available language features could make it smaller again.
B was useful, but it had an important limitation.
It was based on the word-oriented model inherited from BCPL, while the newer machine being targeted, the DEC PDP-11, was fundamentally byte-addressed.
That difference would become one of the important reasons C evolved beyond B.
The PDP-11 Changed the Problem
The PDP-11 arrived at Bell Labs around 1970, and B was moved to it almost immediately.
At first glance, moving B to a newer machine might sound like a straightforward upgrade.
It was not.
The PDP-11 handled memory differently from the older environment. It was a byte-addressed machine, while B's underlying model was word-oriented. This became especially important when programmers needed to work naturally with characters and individual bytes.
There was another problem as well: performance.
The early B implementation used interpreted or threaded code. It worked, but the generated programs were considerably slower than equivalent assembly-language programs. That made B unsuitable for rewriting the entire operating system in the way the researchers wanted.
So there were now two connected problems:
B's language model did not fit the PDP-11 hardware particularly well.
B's implementation was not producing code efficiently enough for serious system-level work.
This is where Dennis Ritchie entered the story.
B Becomes NB, and Then C
In 1971, Dennis Ritchie began extending B.
One of the first important changes was the addition of a character type. At the same time, Ritchie rewrote the compiler so that it could generate PDP-11 machine instructions rather than relying on the slower threaded-code approach.
For a while, this extended version was called NB, meaning “New B.”
But adding a character type was only the beginning.
The language gradually gained a more developed type structure and support for concepts such as arrays and pointers. Ritchie eventually felt that the language had become different enough from B to deserve its own name.
He chose the name C.
The name itself is historically interesting. Ritchie explained that the single-letter naming style continued the progression from BCPL to B, while leaving open the exact interpretation of why the new language was called C.
So the simplified family tree is:
BCPL → B → NB → C
But this was not a clean sequence where one finished language was simply replaced by another. These languages evolved through real engineering problems, compiler experiments and changes in the Unix environment.
That distinction is important.
C was not invented in isolation. It was developed while Unix itself was developing.
1970 or 1972? Why Students See Different Years for C
This is one of the most common historical confusions around C.
You may see 1970, 1971, 1972, or even a broader range such as 1969–1973 depending on the source.
These dates do not necessarily represent contradictory histories.
The problem is that the development of C was gradual.
According to Dennis Ritchie's own historical account, C came into existence during 1969–1973, and the most creative period was 1972. He also specifically states that he began extending B in 1971, adding the character type and rewriting the compiler to generate PDP-11 instructions.
A useful timeline is therefore:
| Year | What happened |
|---|---|
| 1969 | Unix development begins; B's lineage begins with Thompson's work |
| 1970 | PDP-11 arrives and B is moved to it |
| 1971 | Ritchie begins the work that becomes C |
| 1972 | Major creative development of C takes place |
| 1973 | Unix's kernel is rewritten in C |
| 1978 | The C Programming Language by Kernighan and Ritchie is published |
| 1989–1990 | C receives its first major formal ANSI/ISO standardization |
So if someone asks, “When was C created?”, there is no need to pretend that one isolated date explains everything.
If a beginner needs a single year for an exam or simple historical answer, 1972 is the conventional answer and corresponds to the most creative period identified by Ritchie himself.
But the more accurate historical answer is:
C evolved between 1969 and 1973, with major development taking place in 1971–72 and the Unix kernel being rewritten in C in 1973.
That is much closer to what actually happened.
The Moment C Proved Itself: Unix Was Rewritten in C
Creating a language is one thing.
Using that language to build an operating system is another.
This is where the history of C becomes especially important.
The early Unix system had originally been written in assembly. B had been useful for experimentation and utilities, but it was not efficient enough to replace assembly throughout the system.
During 1972, Ken Thompson attempted to move the Unix system further toward C. There were still important problems, including limitations in the early C language and difficulties involving data structures and process-control mechanisms. Ritchie continued improving the language and compiler.
Then came the major breakthrough.
In 1973, the Unix kernel was rewritten in C.
Ritchie described this as an important watershed in Unix's development. The effort demonstrated that C was not merely a small experimental language for simple programs. It could describe the core of an operating system while still producing efficient machine-level code.
That changed the significance of C.
C had moved from being an experiment created around Unix to becoming one of the tools that made Unix itself much more portable and maintainable.
And that relationship would become one of the most important reasons both Unix and C spread.
What Was C Actually Designed to Do?
C was designed around a very practical balance.
It needed to provide enough abstraction to make large system programs manageable, while still staying close enough to the machine that programmers could control memory, data representation and performance.
That balance can be seen in several parts of the language.
C gives programmers direct access to concepts such as:
memory addresses through pointers,
different integer and character representations,
arrays and contiguous memory,
structures for organising data,
explicit control over program flow,
manual resource management,
and relatively little runtime machinery imposed by the language itself.
But C does not simply expose hardware and stop there.
It also provides functions, structured control flow, expressions, types and abstractions that make programs much easier to write than raw assembly.
This combination was one of C's most important strengths.
Ritchie later described C as a small language with small compilers, whose types and operations were grounded in the capabilities of real machines while still providing enough abstraction for portability.
That is a much more useful way to understand C than simply calling it “easy” or “low-level.”
// Include header file for input and output functions
#include <stdio.h>
// Execution starts from the main function
int main(void) {
// High-level abstraction: normal variable storing data
int value = 10;
// Low-level feature: pointer storing the memory address of 'value'
int *ptr = &value;
// Print the value stored inside the memory address
printf("Value: %d\n", *ptr);
// Print the memory address where 'value' is stored
printf("Address: %p\n", (void *)ptr);
// Return 0 to signal successful execution to the OS
return 0;
}
Why Is C Often Called a “Middle-Level” Language?
You will often hear C described as a middle-level programming language.
This is a useful teaching description, but it is not an official classification defined by the C standard.
The idea is simple.
Assembly language is extremely close to a particular processor's instruction set. On the other side, many modern high-level languages provide much stronger abstractions and automatically manage many details that C leaves to the programmer.
C sits somewhere between these extremes.
For example, you can write:
int value = 10;
This looks like ordinary high-level programming.
But you can also write:
int *ptr = &value;
Now you are explicitly working with the address of an object in your program's address space.
&value obtains the address of value, and ptr stores that address. Later, using *ptr allows the program to access the value through that pointer.
That small example captures something important about C.
You can think about data in terms of ordinary variables, but you can also deliberately work with addresses and memory relationships.
This is one of the reasons C became so useful for system programming.
And there is an important distinction here: a pointer in a modern program should not be casually described as a physical RAM address. Modern operating systems normally give programs virtual address spaces. What C exposes is the program's address-level model, not necessarily the physical location of the data in RAM.
That distinction becomes extremely important once we start studying pointers and memory later in this series.
C Was Small — and That Was a Feature
Modern programming languages can contain enormous ecosystems around them: package managers, frameworks, runtime environments, libraries, language servers and countless abstractions.
C began from a very different philosophy.
It was deliberately compact.
Ritchie explained that C's small size, relatively simple compiler requirements, machine-oriented operations and ability to remain sufficiently abstract for portability were major parts of its success.
This did not mean C was designed to be primitive.
It meant the language tried not to hide too much.
A programmer could write code that was relatively close to the machine without having to write every machine instruction manually.
That balance was extremely powerful.
From a Unix Tool to a Portable Language
C's future changed when it started moving beyond its original Unix environment.
During the 1970s, C compilers were adapted for other machines, including systems such as the Honeywell 635 and IBM System/370. Libraries also developed alongside the language, including early work that eventually contributed to C's standard input/output facilities.
Then came an even more important development: Unix itself became portable.
Ritchie's history describes portability experiments on systems such as the Interdata 8/32, followed by the much more influential VAX environment. As Unix spread, C spread with it.
This created a powerful relationship:
C made Unix easier to move between machines, and Unix gave C a real-world environment in which its strengths could spread.
The two helped each other.
By the 1980s, C compilers were available across a wide range of machine architectures and operating systems, and C became increasingly important outside Bell Labs and Unix itself.
K&R C: An Important Historical Distinction
There is another term students frequently encounter: K&R C.
It is important not to misunderstand it.
K&R C does not simply mean “the first version of C created by Dennis Ritchie.”
In 1978, Brian Kernighan and Dennis Ritchie published The C Programming Language. It became the first widely available description of the language and served as an important reference for C for years before formal standardization.
Because of this book, the form of C described by its first edition became known as K&R C.
So there is a useful distinction:
Early C → the language evolving during the early Unix years
K&R C → the historical form of C described in the first edition of Kernighan and Ritchie's book
Standard C → later formally standardized versions of the language
This matters because early C was not frozen at one moment. It continued to evolve after the first versions were created.
From K&R C to Standard C
As C spread across different machines, compilers and organisations, having a common definition became increasingly important.
The ANSI X3J11 committee was established in 1983 to develop a C standard. Its work resulted in the ANSI C standard published in 1989, which was subsequently adopted by ISO as ISO/IEC 9899:1990.
This became the historical foundation generally referred to as C89/C90.
C continued to evolve after that, with later standards adding new capabilities and clarifying existing behaviour.
Among the major versions are:
C89/C90 → C99 → C11 → C17/C18 → C23
The details of these standards are not the main point of this first article. What matters here is the larger story:
C started as a practical language for systems work, became closely connected with Unix, spread across different machines, and eventually needed formal standardization because its use had become much broader than its original environment.
Early C vs Modern C
The C you encounter today is not exactly the same language that Ritchie was developing in the early 1970s.
The core philosophy, however, remains recognisable.
Early C was closely tied to:
Unix development,
PDP-11 systems,
operating-system work,
compilers,
utilities,
and resource-constrained machines.
Modern C is used across a much broader range of computing.
It can be found in areas such as:
operating systems,
embedded systems,
firmware,
compilers,
databases,
networking software,
device software,
performance-sensitive applications,
scientific and engineering software,
and many foundational software components.
The important point is not that C is used everywhere or that it is automatically the best choice for every modern project.
It is that the problems C was designed to solve—control, efficiency, memory, portability and interaction with the underlying machine—have not disappeared.
The machines became dramatically more powerful.
The need to understand how software actually works did not.
Why C Still Matters to a Student
For a beginner, the biggest value of learning C is not simply being able to say:
“I know another programming language.”
The deeper value is understanding what happens underneath the abstractions that modern languages often provide.
When you learn C properly, concepts such as variables, data types, memory, addresses, pointers, arrays, functions, compilation and data representation stop being completely separate topics.
They begin to connect.
You start seeing that a program is not just a collection of instructions written for a compiler. It is also a piece of software that eventually becomes machine code, occupies memory, manipulates data and interacts with an operating system and hardware environment.
That is exactly the kind of understanding that made C valuable in the first place.
And this is why C remains especially useful for students of computer science and related fields.
It forces you to understand the relationship between code and the machine.
The Real Story of C
The history of C is therefore not really the story of one programmer sitting down and inventing a language from scratch.
It is a story of gradual engineering.
Unix needed a better way to be built.
Assembly provided too little abstraction.
B provided a simpler programming model, but its implementation and word-oriented design created problems on the PDP-11.
Ritchie extended B, introduced a type structure, developed a compiler capable of producing efficient machine code, and eventually gave the language a new identity: C.
Then C was used to rewrite the Unix kernel.
That experiment succeeded.
Unix became more portable.
C travelled with it.
The language was documented through K&R, standardized through ANSI and ISO, and eventually became one of the most influential programming languages in computing history.
The remarkable part is that C's original motivation was not to create a fashionable programming language.
It was to build useful software on real machines under real constraints.
That practical origin explains a lot about C's personality even today.
It is small.
It is direct.
It gives the programmer control.
It does not try to hide every machine-level detail.
And sometimes, that is exactly what you need.
What You Should Remember From Part 01
If you remember only a few things from this chapter, remember these:
C did not appear suddenly in 1972. Its development happened across several years, roughly 1969–1973, with 1972 being its most creative period.
C grew alongside Unix. The two projects developed in parallel, and C eventually became the language in which the Unix kernel was rewritten.
C came through a family of languages. The important lineage is BCPL → B → NB → C.
The PDP-11 mattered. Its byte-oriented architecture exposed limitations in B and helped push the development of C's type system.
K&R C is a historical form of C, named after Kernighan and Ritchie's 1978 book, not simply another name for the original C invented in the early 1970s.
And perhaps most importantly:
C was created to solve real systems-programming problems.
That is the mindset we will carry into the rest of this series.
Primary Sources & Further Reading
Dennis M. Ritchie — The Development of the C Language — the most important historical source for understanding how C evolved from BCPL and B and why its design took the shape it did. Read the Bell Labs/Nokia archive
Dennis M. Ritchie — Early Unix history and evolution — useful for understanding the relationship between Unix, the PDP-7, PDP-11 and the development of C. Read the Unix history archive
Bell Labs — From B language to NB to C — a concise historical account of the transition from B to NB to C and the eventual rewrite of Unix in C. Read the Bell Labs Unix archive
The C Programming Language — 1978 Bell Labs paper — an early published description of C and its use in Unix. Read the original Bell Labs publication