05.02.2026 • 9 min read

Fundamentals of Programming | Every Developer Should Know

Cover Image

Introduction

Good software starts with solid fundamentals. This guide is a lecture sheet for CS students, newbie web devs, backend/frontend engineers, and anyone picking up programming. We’ll cover core programming concepts, then dive deep into C—a language that teaches you memory, pointers, and how computers actually work.

Whether you’re catching up on classes or learning from scratch, read this top-to-bottom. Each section builds on the last.


Part 1: Core Programming Fundamentals

graph LR
  Problem[Problem] --> Abstraction[Abstraction]
  Abstraction --> DataStructure[Data Structure]
  DataStructure --> Algorithm[Algorithm]
  Algorithm --> Code[Code]
  Code --> Test[Test & Debug]
  Test --> Solution[Solution]

1.1 What is Programming?

Programming is teaching a computer to solve a problem by breaking it into steps and expressing those steps as code. At the core:

  • Input: data you give the program
  • Processing: logic to transform input
  • Output: results you get back

Example: write a program that takes a list of numbers and returns the sum.


1.2 Abstraction & Decomposition

Abstraction means hiding complexity and focusing on what matters.

  • Break a large problem into smaller, manageable chunks (functions, modules).
  • Each piece should have a clear responsibility and a simple interface.
  • Hide implementation details; users of your code only need to know what it does, not how.

Example:

// Instead of writing the same sum logic 10 times, abstract it into a function:
int sum_array(int arr[], int size) {
  int total = 0;
  for (int i = 0; i < size; i++) {
    total += arr[i];
  }
  return total;
}

// Now use it anywhere:
int result = sum_array(my_numbers, 5);

1.3 Data Structures: storing and organizing data

Different structures suit different jobs:

  • Arrays: fixed-size collections, indexed access O(1).
  • Linked Lists: dynamic size, traversal O(n), insertion O(1) if position known.
  • Stacks: LIFO (Last In First Out), useful for undo/redo.
  • Queues: FIFO (First In First Out), useful for scheduling.
  • Hash Tables: key-value lookup O(1) average.
  • Trees: hierarchical, binary search trees for range queries.
  • Graphs: relations and networks.

Choose based on your access patterns: if you need fast lookup by key, use a hash table; if you need sorted order, use a tree.


1.4 Algorithms: steps to solve the problem

An algorithm is a sequence of steps to achieve a goal.

  • Sorting: quicksort, mergesort, heapsort.
  • Searching: linear search, binary search.
  • Graph traversal: BFS (breadth-first), DFS (depth-first).
  • Dynamic programming: break problem into overlapping subproblems.

1.5 Complexity Analysis (Big-O)

How does runtime or memory grow with input size?

  • O(1): constant time (array access by index).
  • O(log n): logarithmic (binary search).
  • O(n): linear (iterate once).
  • O(n²): quadratic (nested loops).
  • O(2^n): exponential (avoid if possible).

Tip: understand complexity early; a slow algorithm ruins performance at scale.


Part 2: Getting Started with C

C is a small, powerful language that teaches you how programs really work. You’ll learn memory, pointers, and hardware close to the metal. This knowledge transfers to any language.

2.1 Your First C Program

#include <stdio.h>

int main() {
  printf("Hello, World!\n");
  return 0;
}
  • #include <stdio.h>: include the standard I/O library.
  • int main(): entry point; returns an integer status (0 = success).
  • printf(): print formatted text.
  • return 0;: tell OS the program succeeded.

Compile and run:

gcc hello.c -o hello
./hello

2.2 C Keywords (the language building blocks)

C has only ~32 keywords:

  • Data types: int, float, char, double, void.
  • Flow control: if, else, for, while, do, switch, case, break, continue.
  • Functions: return, void.
  • Storage: static, extern, auto, register.
  • Type qualifiers: const, volatile.
  • Structures: struct, union, enum.
  • Pointers: * (dereference), & (address-of).

You don’t need to memorize all; we’ll cover them as we go.


2.3 Variables & Basic Data Types

A variable is a named location in memory holding a value.

Declaration: int age = 25;

  • Type: int (integer)
  • Name: age
  • Value: 25

Common types:

TypeSizeRangeExample
char1 byte-128 to 127'A', '1'
int4 bytes-2^31 to 2^31-142, -10
float4 bytesdecimal3.14
double8 bytesprecise decimal3.14159265359
long4-8 byteslarger integers1000000L

2.4 Input/Output (I/O)

Read and write data.

Output:

#include <stdio.h>

int main() {
  int age = 25;
  float height = 5.9;
  
  printf("Age: %d\n", age);
  printf("Height: %.1f feet\n", height);
  
  return 0;
}

Format specifiers:

  • %d: integer
  • %f: float
  • %s: string
  • %c: character

Input:

int age;
printf("Enter your age: ");
scanf("%d", &age);  // & = address of age

2.5 Control Flow (if, loops)

Conditional:

int score = 75;

if (score >= 90) {
  printf("A\n");
} else if (score >= 80) {
  printf("B\n");
} else {
  printf("C\n");
}

Loops:

// for loop (iterate n times)
for (int i = 0; i < 5; i++) {
  printf("%d\n", i);
}

// while loop (repeat while condition true)
int count = 0;
while (count < 5) {
  printf("%d\n", count);
  count++;
}

// do-while (runs at least once)
int x = 0;
do {
  printf("This runs at least once\n");
  x++;
} while (x < 5);

Part 3: Functions

Functions are reusable blocks of code.

// Define a function
int add(int a, int b) {
  return a + b;
}

// Call it
int result = add(3, 5);  // result = 8

Parts:

  • Return type: int (what it returns).
  • Name: add.
  • Parameters: int a, int b.
  • Body: code between { }.

Why use functions?

  • Reusability
  • Readability
  • Testability

Part 4: Memory & Pointers

This is critical. Pointers are why C is powerful and tricky.

4.1 Memory layout

Every variable lives at an address in memory.

int age = 25;

// & = "address of"
printf("Value: %d\n", age);      // 25
printf("Address: %p\n", &age);   // 0x7ffc8b2b4a4c (example)

4.2 Pointers

A pointer is a variable that holds an address.

int age = 25;
int *p = &age;  // p points to age

printf("p holds address: %p\n", p);      // same as &age
printf("value at p: %d\n", *p);          // 25 (* = dereference)

Why pointers?

  • Dynamic memory allocation
  • Passing variables by reference (modify original, not copy)
  • Building complex data structures (linked lists, trees)

4.3 Dynamic memory

Stack (automatic cleanup) vs heap (you manage).

#include <stdlib.h>

// Allocate
int *arr = (int *)malloc(5 * sizeof(int));
arr[0] = 10;
arr[1] = 20;

// Free (critical!)
free(arr);
arr = NULL;

Important: always free() what you malloc().


Part 5: Arrays & Strings

5.1 Arrays

Fixed-size collection of same type.

int scores[5] = {90, 85, 88, 92, 78};

// Access
printf("%d\n", scores[0]);  // 90

// Iterate
for (int i = 0; i < 5; i++) {
  printf("%d\n", scores[i]);
}

5.2 Strings

In C, strings are char arrays ending with \0.

char name[20] = "Alice";
// Actually: A-l-i-c-e-\0 (null terminator)

// Read string
scanf("%s", name);

// Print string
printf("Name: %s\n", name);

Use <string.h> for operations:

#include <string.h>

strlen(name);      // length
strcpy(dest, src); // copy
strcat(a, b);      // concatenate
strcmp(a, b);      // compare

Group multiple data types together.

struct Person {
  char name[50];
  int age;
  float height;
};

// Create instance
struct Person p1;
strcpy(p1.name, "Alice");
p1.age = 25;

// Or use pointer
struct Person *p2 = (struct Person *)malloc(sizeof(struct Person));
strcpy(p2->name, "Bob");  // -> accesses through pointer
p2->age = 30;
free(p2);

Part 7: Standard Library (STL) vs Manual Approaches

C has a standard library with useful functions.

7.1 Common libraries

  • <stdio.h>: input/output
  • <stdlib.h>: memory allocation, random numbers
  • <string.h>: string operations
  • <math.h>: math functions (sin, cos, sqrt, etc.)
  • <time.h>: time and date
  • <assert.h>: assertions (for debugging)

7.2 STL vs Manual

Using STL (safe, tested):

#include <string.h>
char name[50];
strcpy(name, "Alice");  // Copy string safely

Manual (error-prone, avoid):

// Don't do this without bounds checking:
char name[10];
strcpy(name, "This is a very long string that overflows");  // CRASH!

Lesson: use library functions when available; they’re tested and safe.


Part 8: Common Data Structure Implementations

8.1 Linked List

struct Node {
  int data;
  struct Node *next;
};

// Create a list
struct Node *head = (struct Node *)malloc(sizeof(struct Node));
head->data = 10;
head->next = NULL;

// Add to front
struct Node *new_node = (struct Node *)malloc(sizeof(struct Node));
new_node->data = 5;
new_node->next = head;
head = new_node;

8.2 Stack (simple array-based)

#define MAX 100

int stack[MAX];
int top = -1;  // empty

void push(int x) {
  if (top < MAX - 1) {
    stack[++top] = x;
  }
}

int pop() {
  if (top >= 0) {
    return stack[top--];
  }
  return -1;  // error
}

Part 9: Algorithms with Complexity Examples

9.1 Sorting

Bubble Sort (O(n²), simple but slow):

void bubble_sort(int arr[], int n) {
  for (int i = 0; i < n - 1; i++) {
    for (int j = 0; j < n - i - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        // swap
        int temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
    }
  }
}

Binary Search (O(log n), requires sorted array):

int binary_search(int arr[], int n, int target) {
  int left = 0, right = n - 1;
  while (left <= right) {
    int mid = (left + right) / 2;
    if (arr[mid] == target) {
      return mid;
    } else if (arr[mid] < target) {
      left = mid + 1;
    } else {
      right = mid - 1;
    }
  }
  return -1;  // not found
}

Part 10: Debugging & Testing

10.1 Debugging techniques

#include <assert.h>

int divide(int a, int b) {
  assert(b != 0);  // abort if b is 0
  return a / b;
}

Print debugging:

int result = some_function();
printf("DEBUG: result = %d\n", result);

Use a debugger (GDB):

gcc -g myprogram.c -o myprogram
gdb ./myprogram
(gdb) break main
(gdb) run
(gdb) print variable_name
(gdb) step

10.2 Testing

Write small tests to verify functions:

void test_add() {
  assert(add(2, 3) == 5);
  assert(add(0, 0) == 0);
  assert(add(-1, 1) == 0);
  printf("add() tests passed\n");
}

Part 11: Common Pitfalls & Best Practices

PitfallWhy badFix
Forgetting to free memoryMemory leakCall free() for every malloc()
Buffer overflowCrash/securityUse bounds checking or safer functions
Uninitialized variablesUndefined behaviorAlways initialize: int x = 0;
Not checking malloc returnCrashif (ptr == NULL) { ... }
Pointer dereference errorsSegmentation faultVerify pointer is valid before *ptr

Part 12: From C to Other Languages

Once you master C, other languages feel familiar:

  • C++: adds objects and STL containers (vectors, maps, etc.).
  • Java/C#: memory is auto-managed (garbage collected).
  • Python/JavaScript: same concepts, simpler syntax.

Fundamentals apply everywhere.


Next Steps & Resources

  1. Write small C programs (loop exercises, arrays, functions).
  2. Implement a linked list and a stack from scratch.
  3. Solve 20 algorithmic problems (LeetCode, Project Euler).
  4. Read Kernighan & Ritchie’s “The C Programming Language.”
  5. Build a small project (calculator, task manager, file parser).

Conclusion

Fundamentals are everything. Spend time understanding memory, pointers, data structures, and complexity. These skills transfer across languages and decades of programming.

Good luck, and happy coding! 🚀