Please wait
Please wait
A comprehensive cheatsheet for the C programming language. This guide covers the compilation process, primitive data types, control flow, function design, pointer arithmetic, manual memory management (stack vs. heap), structs, and low-level file I/O operations.
Understanding the anatomy of a C program and the build process
basicsThe Compilation Pipeline: C is a compiled language. Source code (.c) goes through preprocessing (handling macros and includes), compilation (assembly generation), and linking (combining object files and libraries) to produce an executable. The entry point of every C program is the main() function. Preprocessor directives, such as #include, signal the compiler to include standard libraries like stdio.h for input/output operations before compilation begins.
// Basic C Program Structure
#include <stdio.h> // Preprocessor directive
// Function prototype (optional if defined before usage)
void display_message();
// Main entry point
int main() {
// Single-line comment
/* Multi-line
comment */
printf("Hello, World!\n"); // Standard Output
display_message();
return 0; // Exit status 0 indicates success
}
void display_message() {
printf("C is a procedural language.\n");
}// Compilation Commands (GCC)
// Compile and link to create executable 'app'
// gcc source.c -o app
// Execute
// ./appPrimitives, modifiers, and variable scope
typesType System: C is statically typed. Variables must be declared with a specific type before use. The size of these types (e.g., int, long) is platform-dependent. Modifiers like unsigned, const, and static alter the range, mutability, or storage duration of variables. volatile is used to tell the compiler not to optimize access to a variable, often used in embedded systems or hardware interfacing.
// Primitive Types
int id = 100; // Integer
float salary = 5000.50f; // Single-precision float
double precise = 5000.5055; // Double-precision float
char grade = 'A'; // Single character (ASCII)
// Modifiers
unsigned int positive_only = 400; // No negative values
long long big_number = 123456789LL;
const float PI = 3.14159; // Read-only variable
// Storage Classes
// static: Preserves value between function calls, limits scope to file
static int internal_counter = 0;
// extern: Declares a variable defined in another translation unit
extern int shared_global;// Sizeof Operator
// Returns the size in bytes of a type or variable
size_t int_size = sizeof(int); // Typically 4 bytes on 32/64-bit systemsHandling stdin and stdout streams
ioStandard I/O: Input and output in C are handled via streams. The printf and scanf functions from stdio.h are used for formatted output and input, respectively. Format specifiers act as placeholders that tell the function how to interpret the data (e.g., as an integer, string, or hexadecimal value). Security Note: scanf can be dangerous due to buffer overflows; distinct care must be taken with string inputs.
// Format Specifiers
// %d or %i : Integer
// %f : Float
// %lf : Double
// %c : Character
// %s : String
// %p : Pointer address
// %x : Hexadecimal
int age;
float height;
printf("Enter age and height: ");
// Note: &age passes the address of age to scanf
scanf("%d %f", &age, &height);
printf("Age: %d, Height: %.2f\n", age, height);Conditionals and iteration constructs
control-flowLogic Flow: C provides standard control structures. The if-else block handles conditional branching, while switch is optimized for checking a single variable against multiple constant integral values. Loops (for, while, do-while) handle iteration. break exits the immediate loop or switch, while continue skips the current iteration and jumps to the loop condition check.
// If-Else
if (score > 90) {
grade = 'A';
} else if (score > 80) {
grade = 'B';
} else {
grade = 'C';
}
// Ternary Operator
int max = (a > b) ? a : b;
// Switch Statement
switch (option) {
case 1:
start_process();
break; // Crucial to prevent fall-through
case 2:
stop_process();
break;
default:
printf("Invalid option");
}// For Loop (Initialization; Condition; Increment)
for (int i = 0; i < 10; i++) {
printf("%d ", i);
}
// While Loop
while (active) {
process_data();
if (error_detected) active = 0;
}
// Do-While (Guarantees execution at least once)
do {
input = read_sensor();
} while (input < threshold);Contiguous memory allocation and null-terminated strings
arraysArrays: An array is a collection of elements of the same type stored in contiguous memory locations. C does not perform bounds checking; accessing an index outside the declared size leads to undefined behavior. Strings in C are simply one-dimensional arrays of characters terminated by a null character \0. Functions in string.h are used to manipulate these character arrays.
// Array Declaration
int numbers[5] = {10, 20, 30, 40, 50};
numbers[0] = 15; // Modification
// Multi-dimensional Array
int matrix[3][3] = {
{1, 0, 0},
{0, 1, 0},
{0, 0, 1}
};
// Strings (Null-terminated)
char name[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
char city[] = "New York"; // Compiler adds \0 automatically
// String Functions (<string.h>)
#include <string.h>
size_t len = strlen(city); // Length excluding \0
char dest[20];
strcpy(dest, city); // Copy string
strcat(dest, " City"); // Concatenate
int result = strcmp(city, "Paris"); // Compare (0 if equal)Direct memory manipulation and pointer arithmetic
pointersPointers: A pointer is a variable that stores the memory address of another variable. They are fundamental to C for dynamic memory allocation, array manipulation, and passing large data structures to functions efficiently (by reference). The & operator retrieves an address, while the * operator dereferences an address to access the value stored there. Pointer arithmetic allows navigation through contiguous memory blocks like arrays.
// Pointer Syntax
int val = 42;
int *ptr = &val; // ptr holds the address of val
// Dereferencing
printf("Value: %d", *ptr); // Prints 42
*ptr = 100; // Changes 'val' to 100
// Pointer Arithmetic
int arr[] = {10, 20, 30};
int *p = arr; // Points to arr[0]
p++; // Moves to next integer (arr[1])
printf("%d", *p); // Prints 20
// Null Pointer
int *safe_ptr = NULL; // Initialize to avoid garbage values// Pointers to Pointers
int **double_ptr = &ptr; // Holds address of ptr
// Void Pointers (Generic)
void *generic_ptr;
// Cannot be dereferenced directly; must be cast first
int value = *(int*)generic_ptr;Modularizing code and pass-by-reference
functionsFunctions: C functions allow code reuse and modularity. Arguments are passed by value by default (a copy is made). To modify the original variable in the calling function, you must pass the address of the variable (pass by reference via pointers). Scope determines the visibility of variables; local variables exist only within the block they are defined in, while global variables are accessible throughout the program (unless shadowed).
// Pass by Value
void add_one(int n) {
n++; // Only modifies local copy
}
// Pass by Reference (using Pointers)
void add_one_ref(int *n) {
(*n)++; // Modifies the value at the address
}
int main() {
int x = 5;
add_one(x); // x is still 5
add_one_ref(&x); // x becomes 6
}
// Function Pointers
// Used for callbacks and dynamic function calls
int (*func_ptr)(int, int);
func_ptr = &add_numbers; // Assume add_numbers exists
int result = func_ptr(5, 10);Dynamic memory allocation with malloc and free
memoryDynamic Allocation: Automatic variables are stored on the Stack and cleaned up automatically. For data that must persist beyond a function call or has a size determined at runtime, C uses the Heap via stdlib.h functions. malloc allocates raw bytes, calloc allocates and zeroes memory, and realloc resizes an existing block. Every allocation must be paired with free to prevent memory leaks.
// Header required
#include <stdlib.h>
// Malloc: Allocate memory (uninitialized)
int *arr = (int*)malloc(5 * sizeof(int));
if (arr == NULL) {
// Handle allocation failure
}
// Calloc: Allocate and initialize to zero
int *clean_arr = (int*)calloc(5, sizeof(int));
// Realloc: Resize existing block
arr = (int*)realloc(arr, 10 * sizeof(int));
// Accessing dynamic memory
arr[0] = 50;
// Free: Release memory back to OS
free(arr);
free(clean_arr);
// Dangling Pointer prevention
arr = NULL;Creating user-defined data structures
structsStructs: Structures allow grouping variables of different types under a single name. They are essential for modeling complex data objects. Unions allow storing different data types in the same memory location (mutually exclusive). Enums define a set of named integer constants for code readability. typedef is frequently used to create aliases for these types to avoid repetitive typing of the struct keyword.
// Struct Definition
struct Point {
int x;
int y;
};
// Typedef for cleaner syntax
typedef struct {
char name[50];
int id;
float gpa;
} Student;
// Usage
struct Point p1 = {10, 20};
Student s1;
// Accessing members
s1.id = 101;
strcpy(s1.name, "Alice");
// Pointers to Structs
Student *s_ptr = &s1;
// Arrow operator -> dereferences and accesses member
printf("ID: %d", s_ptr->id);
// Enum
typedef enum {
LOW, // 0
MEDIUM, // 1
HIGH // 2
} Priority;
Priority current_status = HIGH;Reading from and writing to files
filesFile Operations: C uses FILE pointers to handle file streams. Common operations include fopen (open file), fprintf (write formatted text), fscanf (read formatted text), fgets (read string), and fclose (close file). File modes include "r" (read), "w" (write/overwrite), "a" (append), and binary modes like "rb" or "wb". Always check if the file pointer is NULL after opening to ensure the file exists or permission was granted.
// Writing to a file
FILE *fp = fopen("data.txt", "w"); // Open for writing
if (fp != NULL) {
fprintf(fp, "ID: %d, Score: %d\n", 1, 95);
fputs("End of log.", fp);
fclose(fp); // Always close
}
// Reading from a file
FILE *fp_read = fopen("data.txt", "r");
char buffer[100];
if (fp_read != NULL) {
// Read line by line
while (fgets(buffer, sizeof(buffer), fp_read)) {
printf("%s", buffer);
}
fclose(fp_read);
}
// Binary I/O
// fwrite(&data, sizeof(data), 1, fp);
// fread(&buffer, sizeof(buffer), 1, fp);Macros and conditional compilation
preprocessorThe Preprocessor: Before compilation, the preprocessor handles directives starting with #. #define creates constants or macros (text substitution). #ifdef, #ifndef, and #endif are used for conditional compilation, often to prevent header files from being included multiple times (include guards).
// Constants
#define MAX_BUFFER 1024
#define PI 3.14159
// Macros (Text substitution - use parenthesis carefully)
#define SQUARE(x) ((x) * (x))
// Conditional Compilation
#ifdef DEBUG
printf("Debug mode on\n");
#endif
// Include Guard (Standard pattern in .h files)
#ifndef MY_HEADER_H
#define MY_HEADER_H
// Structs and prototypes here
#endif