Module 1: Introduction to C
A Brief History of C
The C programming language was created by Dennis Ritchie at Bell Labs in the early 1970s, originally implemented on a DEC PDP-11 computer running the UNIX operating system. It evolved from earlier programming languages that influenced its design.
Language Evolution
- BCPL (Basic Combined Programming Language): Created by Martin Richards — simple, machine-independent system language.
- B: Developed by Ken Thompson, based on BCPL — used for early UNIX development.
- C: Introduced by Dennis Ritchie — added types, structures, and efficiency suitable for system programming.
- Used to rewrite UNIX: C replaced assembly language for the UNIX kernel, improving portability.
- Designed for efficiency: Offers low-level memory access with high-level language features.
- Foundation for many languages: Influenced C++, Java, C#, Objective-C, and more.
Key Milestones in C Standardization
| Year | Standard | Significance |
|---|---|---|
| 1972-1978 | Early C / K&R C | Defined by Brian Kernighan and Dennis Ritchie in "The C Programming Language" |
| 1989 | ANSI C (C89) | First standardized version by ANSI; introduced portability and standard libraries |
| 1990 | ISO C (C90) | Adopted internationally by ISO, identical to ANSI C |
| 1995 | Amendment 1 | Added wide-character support and new library functions |
| 1999 | C99 |
Introduced inline functions, variable-length arrays,
and new data types like long long
|
| 2011 | C11 | Added multi-threading support, Unicode, and improved memory handling |
| 2018 | C18 | Minor bug fixes and clarifications to the C11 standard |
- Portability: C programs can run on multiple hardware platforms with minimal changes.
- Legacy and Longevity: C remains one of the most widely used languages in system and embedded programming.
- Influence: Most modern programming languages have roots in or are heavily inspired by C.
Did You Know? Over 90% of modern operating systems, compilers, and embedded systems are written in C or C-based languages.
C as a Middle-Level Language
C combines the best elements of high-level languages with the control and flexibility of assembly language.
Programming Language Spectrum:
| Level | Languages | Characteristics |
|---|---|---|
| High Level | Ada, Modula-2, Pascal, COBOL, FORTRAN, BASIC | Abstracted from hardware, easier to read and write |
| Middle Level | Java, C++, C, FORTH, Macro-assembler | Balance between hardware control and programming convenience |
| Low Level | Assembler | Direct hardware manipulation, machine-specific |
Key Characteristics of C as Middle-Level Language:
- Hardware Access: Allows manipulation of bits, bytes, and addresses
- Portability: Easy to adapt software for different systems
- Flexible Typing: Not strongly typed; permits type conversions
- Minimal Runtime Checking: No automatic array bounds checking
- Direct Memory Manipulation: Ideal for system-level programming
- Compact Keyword Set: C89 has 32 keywords, C99 adds only 5 more
C as a Structured Language
C is a structured programming language that emphasizes compartmentalization of code and data.
Structured vs. Non-structured Languages:
| Non-structured Languages | Structured Languages |
|---|---|
| FORTRAN | Pascal |
| BASIC | Ada |
| COBOL | C++ |
| C | |
| Java | |
| Modula-2 |
Key Structural Features of C:
- Compartmentalization: C allows you to isolate code and data related to specific tasks, improving clarity and reducing errors.
- Functions: Functions are the main structural unit in C, enabling modular design and reusability of code.
-
Code Blocks: Logical groups of statements are
enclosed in curly braces
{ }, treated as one unit of execution. -
Structured Control: C supports
while,do-while, andforloops. The use ofgotois discouraged, unlike older languages such as BASIC or FORTRAN. - Local Variables: Subroutines and functions in C can use local variables, preventing side effects in other parts of the program.
- Modularity: Functions can be written, tested, and reused independently, making C suitable for large projects involving multiple programmers.
- Reusability of Code: Once a function is created, it can be reused in different parts of a program or even across multiple programs, without modification.
- Reduced Use of Global Variables: Unlike BASIC, which relied heavily on global variables, C promotes controlled scope to avoid unintended interactions between program sections.
- Clarity of Algorithms: Code blocks and structured constructs allow complex algorithms to be expressed with simplicity, clarity, and efficiency.
- Freedom of Layout: Unlike old FORTRAN versions, C does not require strict field positions; statements can be placed flexibly in the code for readability.
- Modern Practice: Structured languages like C are widely used today. Non-structured languages are considered outdated and rarely used for serious new projects.
Example of Code Block in C:
if (x < 10) {
printf("Too low, try again.\n");
scanf("%d", &x);
}
The two statements between curly braces form a logical code block
that executes together.
Such blocks ensure that multiple related actions are treated as one
logical unit.
C as a Programmer's Language
Unlike languages designed for non-programmers (like COBOL and BASIC), C was created by and for working programmers.
Advantages for Programmers:
- Few Restrictions: Maximum flexibility with minimal constraints
- Efficiency: Nearly achieves assembly code efficiency with high-level structure
- Stand-alone Functions: Modular programming approach
- Compact Syntax: Small set of powerful keywords
- Systems Programming: Ideal for operating systems and utilities
- Portability: Code can run on different systems with minimal changes
Historical Applications:
- Systems Programming: Operating systems, compilers, editors, linkers
- Embedded Systems: Still predominantly programmed in C
- Foundation for C++: C forms the basis for object-oriented C++
- Continuing Innovation: C99 standard shows ongoing language development
Compilers vs. Interpreters
Understanding the difference between compilation and interpretation is crucial for C programming, as C was specifically designed as a compiled language.
Comparison Table:
| Aspect | Compiler | Interpreter |
|---|---|---|
| Execution Method | Converts entire program to machine code before execution | Reads and executes source code line by line |
| Performance | Faster execution (direct machine code) | Slower execution (line-by-line processing) |
| Development Cycle | Edit → Compile → Run | Edit → Run |
| Error Detection | All errors reported at compile time | Errors detected during execution |
| Portability | Machine-specific executable | Same source code runs on different platforms |
| Memory Usage | More efficient | Less efficient |
C as a Compiled Language:
- Optimized for Compilation: C was specifically designed as a compiled language
- Object Code: Source code translated into machine-executable binary code
- Efficiency: Compiled programs run faster than interpreted ones
- Stand-alone Executables: No need for interpreter during execution
- Limited Use of Interpreters: Mainly for debugging or experimental platforms
Note: While C interpreters exist, serious C development almost always uses compilers for optimal performance and efficiency.
C Keywords and Reserved Words
C89 Standard Keywords (32 Keywords)
Complete List of C Keywords (32 Keywords)
Data Type Keywords
Control Flow Keywords
Storage Class Keywords
Type Qualifiers & Others
C99 Additional Keywords (5 Keywords)
| Keyword | Purpose |
|---|---|
| Bool | Boolean data type |
| Complex | Complex numbers support |
| Imaginary | Imaginary numbers support |
| inline | Function inlining optimization |
| restrict | Pointer optimization qualifier |
Important Notes About Keywords:
-
Case Sensitivity: C is case-sensitive -
elseis a keyword,ELSEis not - Reserved Usage: Keywords cannot be used as variable or function names
- Compiler Extensions: Many compilers add non-standard keywords for specific environments
Storage Class Specifiers in C
Storage classes define how and where a variable is stored, its scope (where it can be used), and its lifetime (how long it exists).
- There are four main storage classes in C:
autoregisterstaticextern
1. auto
- Default for local variables inside functions.
- Created when function starts, destroyed when it ends.
- Stored in memory (RAM).
- No need to write
autoexplicitly.
auto int a = 10; // same as int a = 10;
printf("%d", a);
}
2. register
- Stored in CPU register for faster access.
- Used for frequently used variables like loop counters.
- You cannot use
&to get its address.
for(i=0; i<10; i++) printf("%d ", i);
3. static
- Remembers its value between function calls.
- Local static → keeps value across calls.
- Global static → visible only inside the file.
static int count = 0;
count++;
printf("%d ", count);
}
4. extern
- Used to declare a variable defined in another file.
- Does not create new memory.
- Used for sharing global variables between files.
int num = 10;
// file2.c
extern int num;
printf("%d", num);
Summary
| Keyword | Scope | Lifetime | Stored In | Use |
|---|---|---|---|---|
| auto | Local | Function runs | Memory | Default local variable |
| register | Local | Function runs | CPU Register | Fast access |
| static | Local/File | Entire program | Memory | Retain value / Hide variable |
| extern | Global | Entire program | Memory | Share variable across files |
Structure of a C Program or Basic Concepts of a C Program
All C programs consist of one or more functions, with
main() being the essential starting point.
General Form of a C Program:
[Comments]
[Preprocessor Directives]
[Global Declarations]
[Function Declarations]
[Function Definitions]
main()
{
[Declaration Section]
[Executable Section]
}
-
Comments
- At the beginning of each program is a comment with a short description of the problem to be solved.
- We can use the comments anywhere in the program.
-
The comments section is optional.
Ex: 1. /* Program1: To find the sum of two numbers*/
2. // Program2: To calculate the area and perimeter of circle - The symbol /* and ends with */ represents the multiline comment.
- The symbol // can also be used for representing single line comment.
-
Preprocessor directives
- The preprocessor statements start with # symbol.
-
These statements instruct the compiler to include some of the
files in the beginning of the program.
Ex: #include<stdio.h>
#include<math.h>
are the files that the compiler includes in the beginning of the program. - The line containing #include<stdio.h> tells the compiler to allow our program to perform standard input and output 'stdio' operations.
- The '#include' directive tells the compiler that we will be using parts of the standard function library.
- Information about these functions is contained in a series of 'header files' (stdio.h). .h says this file is a header file.
- The pointed brackets < and > tell the compiler the exact location of this header file.
-
Using the preprocessor directives the user can define the
constants also.
Ex: #define PI 3.142
-
Global Declarations
- The variables that are declared above (before) the main program are called global variables.
- The global variables can be accessed anywhere in the main program and in all the other functions.
-
Function declarations and Definitions
- In this section the functions are declared.
- Immediately after the functions are declared, the functions can be defined.
-
The program header: main()
- Every program must have a main function.
- Always the C program begins its execution from main.
-
Body of the program
-
After the header or top lines is a set of braces ({ and })
containing a series of 'C' statements which comprise the 'body'-
this is called the action portion of the program.
#include <stdio.h>
main(void) {
/* action portion of the program*/
}
- The global variables can be accessed anywhere in the main program and in all the other functions.
-
After the header or top lines is a set of braces ({ and })
containing a series of 'C' statements which comprise the 'body'-
this is called the action portion of the program.
-
Declaration section
- The variables that are used inside the function should be declared in the declaration section.
-
For example, consider the declaration shown below:
int sum=0;
int a;
float b;
Here, the variable sum is declared as an integer variable and it is initialized to zero.
The variable a is declared as an integer variable whereas the variable b is declared as a floating point variable.
-
Executable section
- They represent the instructions given to the computer to perform a specific task.
- The instructions can be input/output statements, expressions to be evaluated, simple assignment statements, control statements such as if statement, for statement etc.
-
Each executable statement ends with “;”. Example: Write a C
program to display “Hello World”.
#include <stdio.h>
void main(void) {
printf(“Hello World”);
}
Key Structural Elements:
- main() function: Required starting point of execution
- Function-based: Programs are organized around functions
-
Modular design:
main()typically calls other functions -
Top-down organization:
main()provides program outline
About the main() Function:
-
Although
mainis not a keyword, treat it as reserved - Don't use
mainas a variable name - Execution always begins at
main() -
Well-written C code uses
main()as a high-level program outline
The C Standard Library and Linking
Why Libraries Are Essential
C provides very few built-in features through its keywords alone. Most
useful tasks — such as input/output, math, or string handling — come
from the
Standard C Library. Without these libraries, even
simple programs like printf("Hello World"); wouldn’t
work!
-
Input/Output operations: Functions like
printf()andscanf()come from<stdio.h>.
➤ Example:
#include <stdio.h>
printf("Enter a number: ");
scanf("%d", &n); -
Mathematical computations: Functions such as
sqrt(),pow(),sin(),cos()are in<math.h>.
➤ Example:
#include <math.h>
double root = sqrt(25);
// root = 5.0 -
Character handling: Functions like
toupper(),tolower(),isdigit()come from<ctype.h>.
➤ Example:
#include <ctype.h>
char upper = toupper('a');
// upper = 'A' -
String manipulation: Functions like
strcpy(),strlen(),strrev()(not standard everywhere) are in<string.h>.
➤ Example:
#include <string.h>
char name[20];
strcpy(name, "C Programming"); -
Memory management: Functions such as
malloc(),calloc(), andfree()come from<stdlib.h>.
➤ Example:
#include <stdlib.h>
int *ptr = (int *)malloc(5 * sizeof(int));
free(ptr);
The Linking Process
When you compile and run your C program, the compiler and linker work together to include both your code and the necessary library functions.
| Step | Description |
|---|---|
| 1. Compilation |
The compiler converts source code (.c file) into
object code (.obj or .o) and remembers
external function names like printf or
sqrt.
|
| 2. Linking |
The linker connects your object file with the appropriate library
files (e.g., stdio.lib, math.lib).
|
| 3. Execution | The final executable contains both your logic and the library code, ready to run as one program. |
Library Characteristics
- Relocatable Format: Library functions use memory offsets (not fixed addresses) so they can work in any program.
-
Standard Minimal Set: Every C compiler provides
core libraries like
stdio.h,stdlib.h,string.h, andmath.h. - Compiler Extensions: Some compilers add extra libraries (e.g., graphics, sound, or system utilities).
-
Custom Libraries: You can create your own libraries
of reusable functions for future projects.
➤ Example: You can compile your ownmylib.cintomylib.aormylib.liband reuse it.
Technical Note: During linking, the linker automatically replaces placeholder offsets with real memory addresses so that all functions are properly connected before the program runs.
Building Blocks of C Programming
Essential Components Summary
| Component | Purpose | Examples |
|---|---|---|
| Keywords | Language fundamentals and control structures | if, while, int, return |
| Standard Library | Pre-built functionality for common tasks | printf(), scanf(), strlen() |
| Functions | Program organization and modularity | main(), user_defined() |
| Linking | Combining program components into executable | Compiler/linker process |
Best Practices for C Program Structure
- Modular Design: Break programs into small, focused functions
- Library Usage: Leverage standard library functions when possible
- Clear main(): Use main() as a high-level program outline
- Reusable Functions: Create your own library functions for repeated tasks
- Standard Compliance: Stick to standard keywords for portability
Separate Compilation in C
What is Separate Compilation?
Separate compilation allows a C program to be divided across multiple source files, with each file compiled independently before being linked together.
Traditional vs Separate Compilation:
| Aspect | Single File Compilation | Separate Compilation |
|---|---|---|
| Program Size | Best for small programs | Ideal for large projects |
| Compile Time | Recompile entire program for any change | Only recompile changed files |
| Team Development | Difficult with multiple programmers | Easy collaboration |
| Code Organization | All code in one place | Logical separation of functionality |
Benefits of Separate Compilation:
- Reduced Compile Time: Only modified files need recompilation
- Team Collaboration: Multiple programmers can work on different files
- Better Organization: Logical grouping of related functions
- Code Reusability: Easily reuse compiled object files
- Modular Development: Isolate and test individual components
Example Project Structure:
project/
├── main.c (main program)
├── utils.c (utility functions)
├── math_ops.c (mathematical operations)
├── io_handlers.c (input/output functions)
└── headers/
├── utils.h
├── math_ops.h
└── io_handlers.h
The C Program Compilation Process
Three-Step Compilation Process:
| Step | Description | Input | Output |
|---|---|---|---|
| 1. Program Creation | Writing source code using a text editor | Program logic and algorithms | .c source files |
| 2. Compilation | Translating source code to machine code | .c source files | .o object files |
| 3. Linking | Combining object files with libraries | .o files + libraries | Executable file |
Compilation Environments:
Integrated Development Environment (IDE)
- Editor, compiler, and linker in one package
- Automatic project management
- Debugging tools included
- Examples: Code::Blocks, Visual Studio, Eclipse
Stand-alone Compiler
- Separate editor required
- Command-line operation
- More control over compilation process
- Examples: GCC, Clang, MSVC command line
Important Requirements:
- Text Files Only: Compilers require plain text files, not word processor documents
- No Control Codes: Files must not contain special formatting characters
- Compiler Specific: Exact commands vary between compilers
- Documentation: Always consult your compiler's documentation
C Program Memory Map
Four Logical Memory Regions:
Conceptual Memory Layout:
Function calls, local variables
(grows downward)
Dynamic memory allocation
(grows upward)
Global and static variables
Program executable instructions
Detailed Memory Regions:
| Memory Region | Contents | Characteristics |
|---|---|---|
| Code/Text Segment | Executable program instructions | Read-only, fixed size |
| Data Segment | Global variables, static variables | Fixed size, persists throughout program |
| Stack |
• Function return addresses • Function arguments • Local variables • CPU state information |
LIFO structure, automatic management |
| Heap | Dynamically allocated memory | Manual management, flexible size |
Quick Summary (Easy to Remember)
- The memory of a C program is divided into 4 main parts — Code, Data, Stack, and Heap.
- Code Segment → contains your compiled program instructions.
- Data Segment → stores global and static variables.
- Stack → used for function calls and local variables (works like a stack of plates).
- Heap → used for dynamic memory (malloc, calloc, etc.), can grow and shrink at runtime.
Important Points to Remember
- Code Segment: Read-only, fixed once the program starts.
-
Data Segment: Divided into two parts:
- Initialized Data → stores variables with assigned values.
- Uninitialized Data (BSS) → stores variables declared but not initialized.
-
Stack:
- Each function call creates a new stack frame.
- Automatically cleaned when the function ends.
- Too many function calls cause a stack overflow.
-
Heap:
-
Used with
malloc(),calloc(),free(). -
Programmer must release memory manually using
free(). - Not released automatically — can cause memory leaks.
-
Used with
-
Direction of Growth:
- Stack → grows downward (towards lower memory).
- Heap → grows upward (towards higher memory).
-
Lifetime:
- Code & Data → exist for the entire program.
- Stack → temporary, per function call.
-
Heap → as long as you allocate (until
free()).
- Easy Way to Remember: "Code writes the logic, Data holds memory, Stack handles calls, Heap grows freely."
Detailed Memory Regions:
| Memory Region | Contents | Characteristics |
|---|---|---|
| Code/Text Segment | Executable program instructions | Read-only, fixed size |
| Data Segment | Global variables, static variables | Fixed size, persists throughout program |
| Stack |
• Function return addresses • Function arguments • Local variables • CPU state information |
LIFO structure, automatic management |
| Heap | Dynamically allocated memory | Manual management, flexible size |
Stack and Heap Memory Management
Stack Memory Characteristics:
- Automatic Management: Memory allocated and freed automatically
- Fast Access: Very efficient memory access
- LIFO Structure: Last-In-First-Out organization
- Function Scope: Tied to function calls and returns
- Limited Size: Fixed maximum size per thread
Heap Memory Characteristics:
- Manual Management: Programmer controls allocation/deallocation
- Flexible Size: Can grow as needed (within system limits)
- Global Access: Accessible from anywhere in program
- Dynamic Allocation: Memory allocated at runtime
- Slower Access: More overhead than stack
Memory Management Functions:
| Function | Purpose | Memory Region |
|---|---|---|
malloc() |
Allocate memory block | Heap |
calloc() |
Allocate and initialize to zero | Heap |
realloc() |
Resize allocated memory block | Heap |
free() |
Release allocated memory | Heap |
Note: The exact physical layout of memory regions varies between different CPU architectures and C implementations, but the logical structure remains consistent across platforms.
Practical Compilation Example (Fedora)
Separate Compilation Workflow:
Step 1: Create Source Files
$ gedit main.c & utils.c & utils.h
// main.c
#include <stdio.h>
#include "utils.h"
int main() {
int result = add(5, 3);
printf("Result: %d\n", result);
return 0;
}
// utils.c
#include "utils.h"
int add(int a, int b) {
return a + b;
}
// utils.h
#ifndef UTILS_H
#define UTILS_H
int add(int a, int b);
#endif
Step 2: Compile Separately
$ cc -c main.c
# Generates main.o
$ cc -c utils.c
# Generates utils.o
Step 3: Link and Run
$ cc main.o utils.o -o program
$ ./program
Key Advantages in Practice:
- Incremental Compilation: Modify only utils.c? Just recompile utils.c and relink.
-
Simple Editing: Use
geditor any editor to manage code easily. - Faster Build: Avoid recompiling unchanged files.
- Clear Output: Separate object files and final executable.
Review of Terms:
- Source code: The text of a program that a user can read, commonly thought of as the program. The source code is input into the C compiler.
- Object code: Translation of the source code of a program into machine code, which the computer can read and execute directly. Object code is the input to the linker.
- Linker: A program that links separately compiled modules into one program. It also combines the functions in the Standard C library with the code that you wrote. The output of the linker is an executable program.
- Library: A file containing the standard functions that your program can use. These functions include all I/O operations as well as other useful routines.
- Compile time: The time during which your program is being compiled.
- Run time: The time during which your program is executing.