Introduction to C Language
From Bell Labs in 1972 to powering every microcontroller on the planet — a complete foundation in the language that runs the world’s hardware.
What is C?
C is a general-purpose, procedural, compiled programming language developed for writing efficient and portable software. It enables programmers to communicate with a computer using human-readable instructions that are then translated into machine code the hardware executes directly.
Unlike natural languages, computers understand only binary — sequences of 0s and 1s. C acts as a precise bridge: you write instructions a human can read, and the compiler converts them into instructions a machine can run.
Instead of writing raw binary:
010010010101010100110010...You write readable C code:
printf("Hello");The compiler automatically converts your C source code into machine-executable instructions.
Why Learn C?
Many of the most important modern programming languages were directly influenced by or built upon C. Learning C means understanding the shared foundation they all rest on.
C++
Extends C with object-oriented programming. Used in game engines, operating systems, and embedded systems.
Java
Syntax heavily inspired by C. Runs on billions of devices from Android phones to bank servers.
C# & Go
Microsoft’s C# and Google’s Go both borrowed their syntax and philosophy directly from C.
Rust
The modern systems language. C-inspired syntax, designed to solve C’s memory safety challenges.
Python
CPython, the most popular Python interpreter, is written entirely in C for maximum performance.
Objective-C
The original language behind Apple’s software. A strict superset of the C language.
Why Was C Created?
Before C existed, programmers who needed control over hardware had to write software directly in Assembly Language — machine-specific low-level instructions:
MOV AX, 5
ADD AX, 10
MOV [result], AXAssembly language was powerful but came with serious problems that limited what programmers could realistically accomplish:
- Extremely fast execution
- Direct hardware control
- Precise memory access
- Minimal overhead
- Extremely difficult to write
- Different for every processor
- Nearly impossible to maintain
- Not portable between machines
Programmers needed a new language that combined the best of both worlds:
- Easier to write than Assembly Language
- Faster than existing high-level languages like FORTRAN or COBOL
- Portable across different hardware architectures
- Efficient enough to write a complete operating system
The evolution from unreadable machine code to Assembly to C — each step made programming more accessible and powerful
History of C
Timeline
// single-line comment.Dennis Ritchie — Creator of C
Dennis Ritchie developed C at Bell Laboratories in New Jersey. His primary motivation was to rewrite the UNIX operating system in a language that was simultaneously efficient and portable. Before C, operating systems were written entirely in Assembly Language, which meant rewriting them from scratch for every new processor architecture.
Ritchie’s decision to write UNIX in C changed computer science permanently. It proved that a high-level language could produce code efficient enough for systems programming, a belief that had previously been considered impossible.
Language Evolution
Evolution from BCPL (1966) to B (1969) to C (1972). C directly spawned C++ and Objective-C, and heavily influenced nearly every modern language.
Features of C
C became the dominant systems programming language because of a unique combination of features. Each one addresses a specific engineering need.
Simple
C has a small, focused keyword set compared to modern languages. The syntax is clean and easy to learn. Even complex programs follow predictable patterns.
Fast
C compiles directly to native machine code with minimal abstraction overhead. This is why operating systems and real-time embedded systems are written in C.
Portable
A C program written on Windows typically needs little or no modification to compile and run on Linux, macOS, or an ARM microcontroller.
Structured
Programs are organized into functions. Each function is a self-contained unit with clear inputs and outputs, improving readability and maintainability.
Modular
Large programs are broken into smaller modules — separate .c files. Each module can be written, tested, and debugged independently.
Middle-Level
C combines high-level constructs (functions, loops, if/else) with low-level capabilities (direct memory access, pointer arithmetic, bit manipulation).
Efficient Memory
Programmers control exactly how memory is allocated, used, and freed. There is no hidden garbage collector consuming resources in the background.
Extensible
C programs can call assembly routines for hardware-critical operations. The language can be extended with external libraries and standard headers.
Middle-Level Language: The Sweet Spot
One of C’s defining characteristics is that it sits between low-level Assembly and high-level languages like Python. This gives it a unique combination of power and accessibility:
- Human-readable syntax
- Functions and structured code
- Standard library functions
- Portable across platforms
- →Direct memory access via pointers
- →Hardware register access
- →Bit-level manipulation
- →Manual memory management
printf("Hello, World!");int *ptr = (int *)0x40021000; /* Access hardware register directly */
*ptr = 0xFF; /* Write to hardware */Applications of C
C is used almost everywhere that performance, reliability, or hardware control is required. Here are the major domains where C is irreplaceable:
Real-World Examples
| Device / System | Uses C? | What C Powers |
|---|---|---|
| ATM Machine | ✅ Yes | Transaction logic, hardware interface, secure I/O |
| Car ECU (Engine Control Unit) | ✅ Yes | Real-time sensor processing, fuel injection control |
| Arduino (Libraries) | ✅ Yes | Core libraries, hardware abstraction layer |
| Linux Kernel | ✅ Yes | Entirely written in C (~99.9%) |
| Printer Firmware | ✅ Yes | Print queue management, hardware control |
| Drone Flight Controller | ✅ Yes | PID algorithms, sensor fusion, real-time control |
| Medical Devices (pacemakers) | ✅ Yes | Safety-critical real-time embedded firmware |
| Satellite Software | ✅ Yes | Guidance systems, telemetry, reliable low-overhead code |
Advantages & Disadvantages
Every language is a set of tradeoffs. Understanding C’s strengths and weaknesses helps you decide when to use it and what pitfalls to watch for.
- Very fast execution speed
- Highly portable across platforms
- Small runtime footprint
- Highly efficient memory use
- Direct hardware and memory access
- Large mature ecosystem of libraries
- Ideal for embedded systems
- Foundation for learning other languages
- No garbage collection (manual memory management)
- No built-in object-oriented programming
- Weak type safety — easy to misuse pointers
- Buffer overflow security vulnerabilities
- Less abstraction than modern languages
- Harder to debug complex memory issues
- No built-in exception handling
- Standard library is relatively minimal
Advantages vs Disadvantages (Comparison Table)
| Advantage | Corresponding Disadvantage |
|---|---|
| Fast execution | Manual memory management required |
| Portable across platforms | No garbage collector — memory leaks possible |
| Efficient memory usage | Pointer-related bugs can be difficult to trace |
| Direct hardware access | Buffer overflow creates security vulnerabilities |
| Small executable size | Less abstraction than Java, Python, or C# |
C gives you enormous power over the computer. It also expects you to use that power carefully. Every byte of memory you allocate, you must also free. Every pointer you use, you must make sure is valid.
Frequently Asked Questions
The Compilation Process
When you write C code and run it, several things happen behind the scenes. The source code you type cannot run directly — it goes through a multi-stage compilation pipeline that transforms it into a machine-executable program.
The Four Stages
The four-stage C compilation pipeline. GCC runs all stages automatically with a single command.
Stage 1: Preprocessing
The preprocessor handles all lines starting with #. It copies the contents of header files into your source, expands macros (#define), removes all comments, and handles conditional compilation (#ifdef). The output is a larger, pure C file (.i extension).
Stage 2: Compilation
The compiler takes the preprocessed C code and translates it into assembly language — a human-readable form of the machine’s instruction set. This stage also performs syntax checking and optimization.
Stage 3: Assembly
The assembler converts assembly language into binary machine code (0s and 1s). The output is an object file (.o) that contains machine code but is not yet ready to run because it has unresolved references to library functions.
Stage 4: Linking
The linker combines your object file(s) with the necessary library code (like the C standard library that contains printf()). It resolves all external references and produces the final executable program.
printf(), the declaration is in <stdio.h> (header file), but the actual compiled code for printf lives in the C standard library (libc). The linker connects them at link time.Structure of a C Program
Every C program, no matter how large or complex, follows the same fundamental organizational structure. Think of these as the six layers that make up any C program:
The six structural sections of a C program, in order from top to bottom
Here is the simplest complete C program that demonstrates this structure:
/*
Program Name : Hello World
Author : Student
Date : 2024
*/ /* 1. Documentation Section */
#include <stdio.h> /* 2. Link Section */
#define MAX_SIZE 100 /* 3. Definition Section */
int total = 0; /* 4. Global Declaration */
int main() /* 5. main() Function */
{
printf("Hello, World!\n");
return 0;
}
int add(int a, int b) /* 6. User-defined Function */
{
return a + b;
}Documentation & Comments
The documentation section contains comments — text that is completely ignored by the compiler but is invaluable for human readers. Good comments explain why something is done, not just what.
Single-Line Comments
Use // to comment out everything from that point to the end of the line:
// This is a single-line comment
// Store student marks
marks = 95;Multi-Line Comments
Use /* to start and */ to end a block comment. Useful for documentation blocks, explanations, and temporarily disabling code:
/*
Program Name : Student Grade Calculator
Author : Naveen Kumar
Date : 2024-01-15
Description : Reads student marks and calculates grades
*/
/*
This is
a multi-line
comment block.
*/Writing Good Comments
Comments should add information that isn’t obvious from the code itself. Commenting what the code does (when it’s already clear) is noise. Commenting why adds real value:
/* outer /* inner */ still outer */ will cause a compiler error because the first */ closes the comment.Header Files & the Link Section
The line #include <stdio.h> tells the preprocessor to include a header file. Think of header files as toolboxes that contain declarations for useful functions and types you can use in your program.
#include <stdio.h> /* Standard Input/Output */
#include <stdlib.h> /* General Utilities */
#include <string.h> /* String Functions */
#include <math.h> /* Mathematical Functions */Common Header Files
| Header File | Purpose | Key Functions |
|---|---|---|
stdio.h | Standard Input/Output | printf, scanf, fopen, fclose |
stdlib.h | General Utilities | malloc, free, exit, atoi |
string.h | String Functions | strlen, strcpy, strcmp, strcat |
math.h | Math Functions | sqrt, pow, sin, cos, abs |
ctype.h | Character Handling | isalpha, isdigit, toupper, tolower |
stdint.h | Fixed-Width Integer Types | int8_t, uint16_t, uint32_t |
stdbool.h | Boolean Type | bool, true, false |
Angle Brackets vs. Quotes
Macros & the Definition Section
The definition section uses #define to create named constants and macros. Unlike variables, #define values are replaced textually by the preprocessor before compilation — they exist at zero runtime cost.
#define PI 3.14159
#define MAX_SIZE 100
#define TRUE 1
#define FALSE 0When the compiler sees MAX_SIZE, it substitutes the value 100 directly. After preprocessing, the code looks like this:
#define is extensively used for hardware register addresses, bit masks, and configuration values. For example: #define LED_PORT 0x40020000Global Declaration Section
Variables declared outside all functions are called global variables. They can be accessed by any function in the same file (and with extern, across files).
#include <stdio.h>
int total = 10; /* Global variable: accessible everywhere */
void printTotal()
{
printf("Total: %d\n", total); /* Can access 'total' */
}
int main()
{
printf("%d\n", total); /* Can also access 'total' */
total = 20; /* Can modify it too */
printTotal(); /* Prints: Total: 20 */
return 0;
}The main() Function
Every standard C program must have exactly one main() function. This is the entry point — the operating system calls main() first when your program starts, regardless of how many other functions exist.
int main()
{
return 0;
}Anatomy of main()
| Part | Meaning |
|---|---|
int | The function returns an integer value to the operating system |
main | The required name — the OS specifically looks for this name |
() | Parentheses indicate this is a function (can contain parameters) |
{ } | Curly braces define the function body — everything inside belongs to main() |
return 0; | Returns 0 to the OS, indicating successful program completion |
0 signals success to the OS. Returning any non-zero value signals an error. You can check this from the command line after running your program: echo $? on Linux/macOS.Your First C Program
Every programmer’s journey begins with Hello, World. Here is the complete, minimal C program:
#include <stdio.h>
int main()
{
printf("Hello World");
return 0;
}Line-by-Line Explanation
| Line | Code | Meaning |
|---|---|---|
| 1 | #include <stdio.h> | Include the Standard Input/Output library so printf is available |
| 2 | (blank line) | Whitespace for readability — ignored by the compiler |
| 3 | int main() | Define the main function. Program execution begins here. |
| 4 | { | Open curly brace — begin the function body |
| 5 | printf("Hello World"); | Call printf to display text on the screen |
| 6 | return 0; | Return 0 to the OS indicating successful completion |
| 7 | } | Close curly brace — end the function body |
hello.c, then run: gcc hello.c -o hello to compile, then ./hello to execute.The printf() Function
printf() (print formatted) is the primary output function in C. It displays text and values to the standard output (your terminal screen).
printf("Text to display");
printf("Value: %d", someVariable);printf("Hello");
printf("World");printf() does not automatically move to the next line. You must explicitly use \n (the newline escape sequence) to break lines.printf("Hello\n");
printf("World");Escape Sequences
Escape sequences are special character combinations that represent non-printable characters or special formatting:
printf("Name\tMarks\n");
printf("Alice\t95\n");
printf("Bob\t88\n");Format Specifiers
When you want to print values stored in variables, you use format specifiers as placeholders inside the string:
#include <stdio.h>
int main()
{
int age = 20;
float gpa = 3.85;
char grade = 'A';
printf("Age: %d\n", age);
printf("GPA: %.2f\n", gpa);
printf("Grade: %c\n", grade);
return 0;
}The scanf() Function
scanf() (scan formatted) reads input from the user via the keyboard. It pauses execution and waits for the user to type a value and press Enter.
scanf("format_specifier", &variable);#include <stdio.h>
int main()
{
int age;
printf("Enter your age: ");
scanf("%d", &age);
printf("Your age is: %d\n", age);
return 0;
}Why the & (Ampersand)?
The & symbol is the address-of operator. It gives scanf() the memory address of the variable, not the variable’s current value. scanf() needs this address so it knows exactly where in memory to write the input value.
& gives scanf the exact memory address where the value should be delivered.& with scanf() is one of the most common beginner mistakes. Without it, your program may crash with a segmentation fault at runtime — the compiler often won’t warn you.Reading Multiple Values
#include <stdio.h>
int main()
{
int age;
float marks;
char grade;
printf("Enter age, marks, and grade: ");
scanf("%d %f %c", &age, &marks, &grade);
printf("Age: %d, Marks: %.1f, Grade: %c\n", age, marks, grade);
return 0;
}Semicolons
In C, every statement must end with a semicolon (;). The semicolon is the statement terminator — it tells the compiler that a statement has ended. Think of it as the full stop (period) at the end of an English sentence.
Exceptions — these do NOT end with semicolons:
- Preprocessor directives:
#include <stdio.h> - Function definitions:
int main() { ... } - Control flow blocks:
if(...) { ... },for(...) { ... }
Case Sensitivity
C is a case-sensitive language. This means that uppercase and lowercase letters are treated as completely different characters. The following three are three different identifiers:
marks /* identifier 1 */
Marks /* identifier 2 */
MARKS /* identifier 3 */camelCase (like studentAge) or snake_case (like student_age). Constants defined with #define use ALL_CAPS.Common Beginner Mistakes (Syntax)
| Mistake | Wrong Code | Correct Code |
|---|---|---|
| Forgetting semicolons | printf("Hello") | printf("Hello"); |
| Missing header file | int main() { printf("Hi"); } | #include <stdio.h> first |
| Wrong case for printf | Printf("Hello"); | printf("Hello"); |
| Missing & in scanf | scanf("%d", age); | scanf("%d", &age); |
Tokens in C
Before the compiler can process your code, it must break it down into its smallest meaningful pieces. These pieces are called tokens.
Consider this English sentence: "I love programming." — the individual words I, love, programming are the meaningful units.
Similarly, when the C compiler sees int age = 20;, it breaks it into these tokens:
The Six Types of Tokens
| Token Type | Examples | Description |
|---|---|---|
| Keywords | int, if, while, return | Reserved words with predefined meaning |
| Identifiers | sum, studentAge, main | Names you give to variables, functions, etc. |
| Constants | 10, 3.14, 'A' | Literal values that don’t change |
| String Literals | "Hello", "C Programming" | Sequences of characters in double quotes |
| Operators | +, -, *, =, == | Symbols that perform operations |
| Special Symbols | ( ), { }, ;, [ ], , | Punctuation with structural meaning |
Keywords
Keywords are reserved words that already have a fixed, predefined meaning in the C language. The compiler uses them to understand program structure. You cannot use them as variable names or identifiers.
int int = 10; /* Error: "int" is a keyword */
int while = 5; /* Error: "while" is a keyword */int number = 10; /* Correct: "number" is a valid identifier */
int count = 5; /* Correct: "count" is a valid identifier */Common C Keywords
Identifiers
Identifiers are names that you create to identify variables, functions, arrays, structures, and other user-defined elements in your program.
age
marks
studentName
totalSalary
calculateAverageRules for Naming Identifiers
There are exactly five rules. Violating any one of them causes a compiler error:
Valid Characters
Can contain letters (A-Z, a-z), digits (0-9), and underscore (_). No other characters allowed.
Cannot Start with a Digit
student1 is valid. 1student is not. Must begin with a letter or underscore.
No Spaces Allowed
studentMarks is valid. student marks is not. Use camelCase or snake_case instead.
Keywords Forbidden
int while = 5; is illegal. Reserved words cannot be used as identifiers.
C is Case-Sensitive
marks, Marks, and MARKS are three completely different identifiers.
Good Naming Style
Meaningful names make your code self-documenting and dramatically easier to maintain:
| Identifier | Valid? | Reason |
|---|---|---|
student | ✅ | Letters only — valid |
student1 | ✅ | Letters and digit — valid |
_count | ✅ | Starts with underscore — valid |
1student | ❌ | Starts with digit — invalid |
student marks | ❌ | Contains space — invalid |
while | ❌ | Is a keyword — reserved |
my-var | ❌ | Hyphen not allowed — invalid |
Variables
A variable is a named memory location that stores data. You give the memory location a name (the variable name) so you can refer to it in your program. When the value changes, the contents of that memory location change.
age labelsa location in RAM
holding the value
20Variable Declaration
Before using a variable you must declare it — tell the compiler its name and data type:
datatype variableName;int age; /* declare an integer variable */
float salary; /* declare a float variable */
char grade; /* declare a character variable */
double pi; /* declare a double variable */Initialization
Giving a variable its first value is called initialization. It can be done at declaration or separately:
/* Declaration only */
int age;
/* Initialization after declaration */
age = 20;
/* Declaration AND initialization in one line */
int score = 95;Declaration vs Initialization
| Operation | Code | What happens |
|---|---|---|
| Declaration | int age; | Creates the variable; value is undefined (garbage) |
| Initialization | age = 20; | Assigns the first value to an existing variable |
| Combined | int age = 20; | Declares and initializes in one step |
Multiple Variables
/* Separate declarations */
int a = 10;
int b = 20;
int c = 30;
/* Combined on one line */
int a = 10, b = 20, c = 30;int count = 0;Constants
A constant is a value that cannot be changed during program execution. Once defined, it remains fixed for the program’s lifetime. Attempting to modify a const variable causes a compiler error.
const float PI = 3.14159;
const int MAX = 100;
PI = 5; /* ERROR: cannot modify a const variable */
MAX = 200; /* ERROR: cannot modify a const variable */Types of Constants
Integer Constants
Whole numbers without decimal points: 10, 200, -15, 0
Floating Constants
Numbers with decimal points: 3.14, 5.67, 100.25, -0.5
Character Constants
Single characters in single quotes: 'A', 'Z', '9', '$'
String Constants
Characters in double quotes: "Hello", "C Programming", "Embedded"
/* Integer constant */
const int MAX_STUDENTS = 50;
/* Floating constant */
const float PI = 3.14159;
/* Character constant */
const char PASS_GRADE = 'A';
/* String constant (stored as char array) */
const char SCHOOL_NAME[] = "Embedded Systems Institute";Variable vs Constant
| Property | Variable | Constant |
|---|---|---|
| Can be changed? | ✓ Yes | ✗ No |
| Declared with | int x; | const int x; |
| Storage | Regular RAM | Often in read-only memory (ROM) |
| Use case | Data that changes during execution | Fixed values like PI, MAX_SIZE |
| Reassignable? | ✓ Yes | ✗ Compiler error |
Good Coding Practices
Writing code that works is the minimum. Writing code that is readable, maintainable, and reliable is the professional standard. These practices apply from your first program onwards:
✓ Use Meaningful Names
✓ Always Initialize Variables
✓ Keep Naming Consistent
✓ Write Comments That Explain Intent
Common Beginner Mistakes
Mistake 1: Using Keywords as Variable Names
Mistake 2: Starting Identifiers with Numbers
Mistake 3: Spaces in Identifiers
Mistake 4: Using Confusing Single-Letter Names
Memory Tricks & Mnemonics
Remember the Six Token Types
Use the mnemonic “Kind Intelligent Cats Sing Opera Softly” to remember all six token types:
Remember Identifier Rules
Ask these four questions before naming an identifier — if you answer YES to all of them, the name is valid:
| Question | Must Be | Example |
|---|---|---|
| Contains only letters, digits, underscore? | YES | student_1 ✅ |
| Does it start with a letter or underscore? | YES | age, _count ✅ |
| No spaces anywhere? | YES | studentAge ✅ |
| Is it NOT a keyword? | YES | count (not while) ✅ |
Remember C Program Structure
Each section appears in this order, from top to bottom of the file
Interview Questions
These questions frequently appear in entrance tests, university exams, and technical interviews for embedded systems and systems programming roles.
int age = 20;, each of int, age, =, 20, and ; is a separate token.int while = 5; is invalid because while is a keyword.- Declaration creates the variable and reserves memory:
int age; - Initialization assigns an initial value:
age = 20; - Both can be done together:
int age = 20;
totalMarks is instantly understandable; a name like t or x is not.marks, Marks, and MARKS as three completely different identifiers. This is also why printf must be all lowercase — writing Printf or PRINTF will cause a compiler error.#include directive tells the preprocessor to include the contents of a header file in your source code. This makes functions like printf() and scanf() available. Without #include <stdio.h>, the compiler would not recognize standard I/O functions.- High-level languages (like Python, Java) — C supports functions, loops, if/else, and formatted I/O.
- Low-level languages (like Assembly) — C supports direct memory access via pointers, bit manipulation, and hardware register access.
return 0; in main() signals to the operating system that the program completed successfully. Any non-zero return value indicates an error. Many shell scripts and build systems check this return code to determine if a program ran correctly.Practice Questions
- Define a token in C. Give one example of each of the six types.
- What is a keyword? List five C keywords and their purpose.
- What is an identifier? What are the five rules for naming one?
- What is a variable? How do you declare and initialize one?
- What is a constant? How is it different from a variable?
- What is the purpose of
#include <stdio.h>? - Write a C program that displays the message "I am learning C".
- Differentiate between variables and constants with code examples.
- Explain all five rules for naming identifiers with valid and invalid examples.
- Explain the difference between declaration and initialization. Show three ways to initialize a variable.
- List the different types of tokens with one example of each.
- What is the compilation process? Describe each of the four stages.
- Explain the six structural sections of a C program with an annotated example.
- Declare variables for: Student Name (string), Student Age (int), Student Marks (float). Initialize them with sample values and print them using
printf(). - Write a program that asks the user to enter their name and age, then prints a greeting: "Hello [Name], you are [Age] years old."
- Declare one variable of each type:
int,float,char,double. Initialize all four with appropriate values and print each with the correct format specifier. - Write a program that defines constants for PI (3.14159) and MAX_STUDENTS (50). Print both values with descriptive labels. Try assigning a new value to PI and observe the compiler error.
Chapter Summary
- C is a general-purpose, procedural, compiled programming language created by Dennis Ritchie at Bell Labs in 1972
- C was created to rewrite the UNIX operating system in a portable, efficient language superior to Assembly
- The language has evolved through BCPL (1966) → B (1969) → C (1972) and continues to be standardized (C23)
- C’s key features: simple, fast, portable, structured, modular, middle-level, and memory-efficient
- C is used in operating systems, embedded systems, device drivers, compilers, databases, and networking
- The compilation pipeline has four stages: Preprocessor → Compiler → Assembler → Linker
- A C program has six structural sections: Documentation, Link, Definition, Global, main(), Functions
- printf() displays output; scanf() reads input; both use format specifiers (%d, %f, %c, %s)
- C has six token types: Keywords, Identifiers, Constants, Strings, Operators, Special Symbols (KICSОС)
- Identifier rules: letters/digits/underscore only; no leading digit; no spaces; not a keyword; case-sensitive
- Variables store changeable data; constants store fixed values declared with
const - C = Power + Responsibility — great control over hardware requires careful, intentional programming
What’s Next?
You have completed Chapter 1 and built a solid foundation. Chapter 2 dives into the heart of C programming — the data model that makes the language so expressive and powerful in embedded systems: