Program structure & compilation
#include <stdio.h> // preprocessor directive
int global_var = 10; // global (optional)
int main(int argc, char *argv[]) {
int x = 5;
printf("x = %d\n", x);
return 0; // 0 = success
}
| Command | Effect |
|---|
gcc file.c -o out | Compile + link β executable |
gcc -c file.c | Compile to object file (.o) |
gcc -S file.c | Compile to assembly (.s) |
gcc file.c -Wall -Wextra | Enable warnings |
./out | Run the program |
| Header | Provides |
|---|
stdio.h | printf, scanf, fopen, fclose, fgets |
stdlib.h | malloc, calloc, free, exit, atoi, qsort |
string.h | strlen, strcpy, strcat, strcmp, strstr |
math.h | sqrt, pow, sin, cos, ceil, floor (link -lm) |
ctype.h | isalpha, isdigit, toupper, tolower |
Data types & variables
| Type | Size | Format | Notes |
|---|
| char | 1 B | %c / %d | -128 to 127 |
| unsigned char | 1 B | %u | 0 to 255 |
| short | 2 B | %hd | -32768 to 32767 |
| int | 4 B | %d | ~-2.1B to 2.1B |
| unsigned int | 4 B | %u | 0 to ~4.3B |
| long | 4/8 B | %ld | platform dependent |
| long long | 8 B | %lld | large integers |
| float | 4 B | %f | ~7 digit precision |
| double | 8 B | %lf | ~15 digit precision |
Declaration & casting
int a = 10;
const int MAX_SIZE = 100; // cannot be modified
int x = 3.7; // truncates -> 3
float y = 5; // widens -> 5.0
float fr = (float)a / b; // explicit cast
printf("%zu\n", sizeof(int)); // sizeof operator -> 4
printf("%d %.2f %s %c\n", num, pi, name, ch);
printf("%10d\n", num); // right-aligned width 10
printf("%-10d|\n", num); // left-aligned
printf("%010d\n", num); // zero-padded
scanf("%d", &age); // & required for scalars
scanf("%49s", name); // no & needed for arrays
char full[100];
fgets(full, sizeof(full), stdin); // reads spaces too
full[strcspn(full, "\n")] = '\0'; // strip newline
sprintf(buf, "Age is %d", age); // format into string
sscanf(data, "%d %f", &x, &y); // parse from string
| Specifier | Type |
|---|
| %d / %i | int |
| %u | unsigned |
| %f | float/double |
| %e | scientific |
| %c | char |
| %s | string |
| %p | pointer |
| %x / %X | hex |
| %o | octal |
| %ld | long |
| %lld | long long |
| %zu | size_t |
Operators
- Arithmetic:
+ - * / % (int / truncates toward zero)
- Relational:
== != < > <= >=
- Logical:
&& || ! β short-circuit evaluated
- Assignment:
+= -= *= /= %=
- Increment:
++x pre (change then use), x++ post (use then change)
- Ternary:
cond ? valIfTrue : valIfFalse
Precedence (high β low, abbreviated): () [] -> . β unary ! ~ ++ -- * & β * / % β + - β shifts << >> β relational β == β & β ^ β | β && β || β ?: β =
Control flow
Conditionals
if (score >= 60) { printf("Pass\n"); }
else if (score >= 40) { printf("Borderline\n"); }
else { printf("Fail\n"); }
switch (day) {
case 1: printf("Mon"); break;
case 6: case 7: printf("Weekend"); break; // fallthrough
default: printf("Invalid");
}
Loops
for (int i = 0; i < 5; i++) { printf("%d ", i); }
while (cond) { ... }
do { ... } while (cond); // body runs at least once
for (int i = 0; i < 10; i++) {
if (i == 5) break; // exit loop entirely
if (i % 2 == 0) continue; // skip to next iteration
printf("%d ", i);
}
Functions
int add(int a, int b); // prototype (declare before use)
int add(int a, int b) { return a + b; } // definition
void swap(int *a, int *b) { // pass by pointer to modify caller
int t = *a; *a = *b; *b = t;
}
int main() {
int x = 10, y = 20;
swap(&x, &y); // x=20, y=10
}
All C arguments are passed by value β pass a pointer to let a function modify the callerβs variable. Good practice: return results instead of printing inside a function; take input as parameters instead of scanf inside.
Recursion
long long factorial(int n) {
if (n == 0 || n == 1) return 1; // base case
return n * factorial(n - 1); // recursive case
}
int fib(int n) {
if (n <= 0) return 0;
if (n == 1) return 1;
return fib(n-1) + fib(n-2); // O(2^n) - slow!
}
Every recursive function needs a base case and a recursive case that moves toward it. Naive recursive Fibonacci is exponential; an iterative version is O(n).
Arrays
int arr[5] = {1, 2, 3, 4, 5};
int mat[3][3] = {{1,2,3},{4,5,6},{7,8,9}};
void print_array(int arr[], int n) { // decays to pointer
for (int i = 0; i < n; i++) printf("%d ", arr[i]);
}
// arr[i] is equivalent to *(arr + i)
// array name = pointer to its first element
Strings
char s[] = "Hello"; // null-terminated: {'H','e','l','l','o','\0'}
strlen(s); strcpy(dst, s); strcat(dst, s); strcmp(a, b);
strstr(s, "sub"); strchr(s, 'l');
atoi("42"); atof("3.14");
toupper(c); tolower(c); isalpha(c); isdigit(c);
Prefer strncpy/strncat/fgets over the unbounded versions to avoid buffer overflows.
Pointers
int x = 42;
int *p = &x; // & = address-of
printf("%d\n", *p); // * = dereference -> 42
int **pp = &p; // pointer to pointer
**pp = 99; // modifies x through pp
int *n = NULL; // always check before dereferencing
if (n != NULL) { ... }
Pointer arithmetic
int arr[] = {10,20,30,40,50};
int *p = arr;
p++; // now points to arr[1]
p += 2; // now points to arr[3]
ptrdiff_t d = (&arr[4]) - (&arr[0]); // d = 4 (element count)
Dynamic memory allocation
int *a = malloc(5 * sizeof(int)); // uninitialized memory
int *b = calloc(5, sizeof(int)); // zero-initialized memory
a = realloc(a, 10 * sizeof(int)); // resize existing block
if (a == NULL) { /* allocation failed */ exit(1); }
free(a); a = NULL; // always free; avoid leaks
A memory leak happens when allocated memory is never freed. Always pair every malloc/calloc with a matching free.
Structures, unions & enums
typedef struct {
int id;
char name[50];
float gpa;
} student_t;
student_t s = {101, "Alice", 3.8};
s.gpa = 3.9; // dot operator
student_t *p = &s;
p->gpa = 4.0; // arrow operator (same as (*p).gpa)
union data { int i; float f; char str[20]; }; // members share memory
typedef enum { MON=1, TUE, WED, THU, FRI, SAT, SUN } weekday_t; // auto-increments
| Property | Structure | Union |
|---|
| Memory | Sum of all members | Size of largest member |
| Active members | All at once | Only one at a time |
File handling
FILE *fp = fopen("data.txt", "w"); // r w a rb wb r+ w+
if (fp == NULL) { perror("fopen"); return 1; }
fprintf(fp, "Score: %.2f\n", 98.5);
fclose(fp);
fp = fopen("data.txt", "r");
char line[100];
while (fgets(line, sizeof(line), fp) != NULL) { printf("%s", line); }
fclose(fp);
fwrite(&rec, sizeof(rec), 1, fp); // binary write
fread(&rec, sizeof(rec), 1, fp); // binary read
fseek(fp, 0, SEEK_END); // SEEK_SET / SEEK_CUR / SEEK_END
long size = ftell(fp);
Preprocessor directives
#define PI 3.14159
#define SQUARE(x) ((x) * (x)) // always parenthesize macro args
#ifdef DEBUG
printf("debug info\n");
#endif
#ifndef HEADER_H // include guard
#define HEADER_H
...
#endif
Bitwise operators
| Operator | Meaning |
|---|
& | AND |
| | OR |
^ | XOR |
~ | NOT |
<< | shift left (Γ2^n) |
>> | shift right (Γ·2^n) |
n & 1 // check if odd (LSB test)
n | (1 << k) // set bit k
n & ~(1 << k) // clear bit k
n ^ (1 << k) // toggle bit k
(n >> k) & 1 // read bit k
c ^ 32 // toggle ASCII letter case
a ^= b; b ^= a; a ^= b; // swap two ints without a temp variable
Storage classes
| Class | Scope | Lifetime | Default value |
|---|
auto | block | block | garbage |
register | block | block | garbage (CPU-register hint) |
static | block/file | whole program | 0 |
extern | file/global | whole program | 0 |
static local variables retain their value between function calls.
Command-line arguments
int main(int argc, char *argv[]) {
// argc = number of arguments (includes program name)
// argv[0] = program name, argv[1..] = actual arguments
for (int i = 0; i < argc; i++) printf("%s\n", argv[i]);
}
Error handling
FILE *fp = fopen("missing.txt", "r");
if (fp == NULL) {
perror("fopen"); // prints readable error
printf("%s\n", strerror(errno)); // errno.h
return 1;
}
if (malloc(100) == NULL) exit(EXIT_FAILURE);
Sorting & searching (patterns)
| Algorithm | Idea | Time |
|---|
| Selection sort | Repeatedly find min, swap into place | O(nΒ²) |
| Bubble sort | Repeatedly swap adjacent out-of-order pairs | O(nΒ²) |
| Merge sort | Divide, sort halves, merge | O(n log n) |
| Quick sort | Partition around a pivot, recurse | O(n log n) avg |
| Linear search | Scan every element | O(n) |
| Binary search | Halve the range on a sorted array | O(log n) |
int binary_search(int arr[], int n, int target) {
int lo = 0, hi = n - 1;
while (lo <= hi) {
int mid = lo + (hi - lo) / 2;
if (arr[mid] == target) return mid;
else if (arr[mid] < target) lo = mid + 1;
else hi = mid - 1;
}
return -1;
}
Common standard library functions
// math.h (compile with -lm)
sqrt(x); pow(b,e); fabs(x); ceil(x); floor(x); round(x); log(x); log10(x);
// stdlib.h
srand(time(NULL)); rand(); // random numbers
int dice = rand() % 6 + 1; // 1-6
exit(0); exit(EXIT_FAILURE);
qsort(arr, n, sizeof(int), cmp_func);
Quick revision summary
- Pointers:
int *p = &x; holds an address; *p dereferences it.
- Arrays:
arr[i] == *(arr + i); array name decays to a pointer when passed to functions.
- Strings: null-terminated char arrays; always leave room for
'\0'.
- Memory: pair every
malloc/calloc with free; check for NULL.
- Structs: dot
. for values, arrow -> for pointers to structs.
- Functions: pass-by-value by default; use pointers to modify caller data.
- Files: always check
fopen for NULL and close with fclose.
Pointers vs arrays β side by side
| Expression | Meaning |
|---|
arr | address of first element (constant, cannot be reassigned) |
*arr | value of first element |
arr[i] | value at index i |
&arr[i] | address of element i |
p = arr | p is a pointer, CAN be reassigned / incremented |
p[i] | same as *(p + i) |
sizeof(arr) | total bytes of the array (compile-time size) |
sizeof(p) | size of the pointer itself (e.g. 8 bytes on 64-bit) |
Worked examples
Reverse an array in place
void reverse(int arr[], int n) {
int i = 0, j = n - 1;
while (i < j) {
int t = arr[i]; arr[i] = arr[j]; arr[j] = t;
i++; j--;
}
}
Find max and min with pointer outputs
void find_max_min(int arr[], int n, int *max, int *min) {
*max = *min = arr[0];
for (int i = 1; i < n; i++) {
if (arr[i] > *max) *max = arr[i];
if (arr[i] < *min) *min = arr[i];
}
}
// call: find_max_min(arr, 5, &max, &min);
Count set bits
int count_ones(unsigned int n) {
int count = 0;
while (n != 0) { count += n & 1; n >>= 1; }
return count;
}
Dynamic 2D array
int **m = malloc(rows * sizeof(int*));
for (int i = 0; i < rows; i++) m[i] = malloc(cols * sizeof(int));
// ... use m[i][j] ...
for (int i = 0; i < rows; i++) free(m[i]);
free(m);
CDAC C-CAT β top C programming traps
| Trap | Rule |
|---|
Missing & in scanf | Forgetting & before a variable in scanf causes crashes or garbage reads. |
| String comparison | Comparing strings with == compares pointers, not contents β use strcmp. |
| Loop bounds | Off-by-one errors, especially <= vs < on array bounds. |
| Returning a local address | Returning the address of a stack variable is invalid β it no longer exists after the function returns. |
| Memory leaks / use-after-free | Forgetting to free allocated memory, or using memory after itβs freed. |
Missing break | A missing break in a switch causes unintended fallthrough. |
| Integer division truncation | 5 / 2 is 2, not 2.5 β cast one operand to float if you need the decimal. |
| Unparenthesized macros | #define SQ(x) x*x breaks for SQ(a+b) β always wrap macro parameters in parentheses. |
| Unbounded string functions | gets/strcpy without a size limit risk buffer overflows β prefer the bounded variants. |
PYQs are indicative of exam style, not guaranteed exact repeats.