Curated questions covering pointers, memory management, arrays, strings, structures, file handling, and data structures in C.
C is a general-purpose procedural language developed by Dennis Ritchie in 1972. Key features: low-level memory access via pointers, efficient execution, portable across platforms, structured programming, rich operators, and it is the foundation for C++, Java, and many other languages.
Compiled languages (C, C++) translate source code to machine code before execution - faster runtime. Interpreted languages (Python, JavaScript) translate at runtime - slower but more flexible. C is compiled: gcc/clang converts .c files to optimized machine code.
printf("%zu\n", sizeof(int)); // 4
printf("%zu\n", sizeof(long)); // 4 or 8
printf("%zu\n", sizeof(char)); // 1
A pointer is a variable that stores the memory address of another variable. Pointers enable dynamic memory allocation, efficient array handling, and passing variables by reference.
int x = 10;
int *ptr = &x; // ptr holds address of x
printf("%d\n", *ptr); // 10 (dereference)
*ptr = 20; // modifies x through pointer
printf("%d\n", x); // 20
int x = 5;
int *p = &x; // & gets address, * declares pointer
printf("%d", *p); // * dereferences to get value 5
void byValue(int x) { x = 100; } // original unchanged
void byRef(int *x) { *x = 100; } // original changed
int n = 5;
byValue(n); // n still 5
byRef(&n); // n is now 100
int arr[5] = {1,2,3,4,5};
int *ptr = arr; // ptr points to arr[0]
printf("%d", *(ptr + 2)); // 3 (pointer arithmetic)
printf("%d", arr[2]); // 3 (same result)
When you add 1 to a pointer, it advances by sizeof(type) bytes, not 1 byte. This allows traversing arrays efficiently.
int arr[] = {10, 20, 30, 40};
int *p = arr;
p++; // advances by sizeof(int) = 4 bytes
printf("%d\n", *p); // 20
printf("%d\n", *(arr + 3)); // 40
A null pointer does not point to any valid memory location. Represented by NULL (0 or (void*)0). Always initialize pointers to NULL and check before dereferencing to avoid undefined behavior.
int *ptr = NULL;
if (ptr != NULL) {
printf("%d", *ptr);
} else {
printf("Null pointer");
}
A dangling pointer points to memory that has been freed or gone out of scope. Dereferencing it causes undefined behavior. Always set pointers to NULL after freeing.
int *ptr = (int*)malloc(sizeof(int));
*ptr = 10;
free(ptr);
ptr = NULL; // prevent dangling pointer
// Also: returning address of local variable
int* bad() { int x = 5; return &x; } // WRONG
int *a = (int*)malloc(5 * sizeof(int)); // uninitialized
int *b = (int*)calloc(5, sizeof(int)); // zero-initialized
a = (int*)realloc(a, 10 * sizeof(int)); // resize
free(a); a = NULL;
A memory leak occurs when dynamically allocated memory is not freed after use. The memory remains allocated but inaccessible, gradually consuming available memory. Use Valgrind to detect leaks.
void leak() {
int *p = (int*)malloc(100 * sizeof(int));
// forgot free(p) - memory leak!
}
void noLeak() {
int *p = (int*)malloc(100 * sizeof(int));
free(p); p = NULL;
}
A structure (struct) groups variables of different types under a single name.
struct Student {
char name[50];
int age;
float gpa;
};
struct Student s1;
strcpy(s1.name, "Alice");
s1.age = 20;
s1.gpa = 3.8;
typedef struct { char name[50]; int age; } Student;
union Data {
int i; // 4 bytes
float f; // 4 bytes
char c; // 1 byte
}; // sizeof = 4 (largest member)
union Data d;
d.i = 10; // only d.i is valid now
#define MAX 100 // no type, no memory
const int MAX = 100; // typed, has address
// #define can cause bugs:
#define SQUARE(x) x*x
SQUARE(2+3) // expands to 2+3*2+3 = 11, not 25!
int globalVar = 10; // global
void func() {
int localVar = 5; // local
printf("%d %d", globalVar, localVar);
}
void counter() {
static int count = 0; // persists between calls
count++;
printf("%d\n", count);
}
counter(); // 1
counter(); // 2
extern declares a variable or function defined in another file. It tells the compiler the definition exists elsewhere, allowing multi-file programs to share variables.
// file1.c
int globalVar = 10;
// file2.c
extern int globalVar; // declaration only
void func() { printf("%d", globalVar); }
A function pointer stores the address of a function and can be used to call it indirectly. Used for callbacks, dispatch tables, and implementing polymorphism in C.
int add(int a, int b) { return a + b; }
int sub(int a, int b) { return a - b; }
int (*op)(int, int); // function pointer
op = add;
printf("%d\n", op(3, 2)); // 5
op = sub;
printf("%d\n", op(3, 2)); // 1
Strings in C are null-terminated character arrays. The standard library (string.h) provides functions for string manipulation.
char str[50] = "Hello";
strlen(str); // 5
strcpy(dest, str); // copy
strcat(str, " World"); // concatenate
strcmp(s1, s2); // compare (0 if equal)
strncpy(dest, src, n); // safe copy with limit
char buf[100];
fgets(buf, sizeof(buf), stdin); // safe
buf[strcspn(buf, "\n")] = 0; // remove trailing newline
C provides FILE* type and functions from stdio.h for file operations.
FILE *fp = fopen("data.txt", "r");
if (fp == NULL) { perror("Error"); exit(1); }
char line[256];
while (fgets(line, sizeof(line), fp)) {
printf("%s", line);
}
fclose(fp);
FILE *fp = fopen("data.bin", "wb");
fwrite(&student, sizeof(Student), 1, fp);
fclose(fp);
fp = fopen("data.bin", "rb");
fread(&student, sizeof(Student), 1, fp);
typedef unsigned int uint; // proper type alias
typedef struct Node* NodePtr;
#define UINT unsigned int
// UINT *p1, p2; -> p2 is NOT a pointer!
A linked list is a dynamic data structure where each node contains data and a pointer to the next node.
struct Node {
int data;
struct Node *next;
};
struct Node* createNode(int data) {
struct Node *n = (struct Node*)malloc(sizeof(struct Node));
n->data = data;
n->next = NULL;
return n;
}
struct Point p = {3, 4};
printf("%d", p.x); // dot: direct access
struct Point *ptr = &p;
printf("%d", ptr->x); // arrow: pointer access
int i = 5;
printf("%d", ++i); // prints 6, i is 6
printf("%d", i++); // prints 6, i becomes 7
printf("%d", i); // prints 7
unsigned int u = 4294967295; // max uint32
int s = -1;
printf("%u", (unsigned int)s); // 4294967295 (same bits)
int a = 5; // 0101
int b = 3; // 0011
printf("%d", a & b); // 1 (0001)
printf("%d", a | b); // 7 (0111)
printf("%d", a ^ b); // 6 (0110)
printf("%d", a << 1); // 10 (1010)
void *vp;
int x = 10;
vp = &x;
printf("%d", *(int*)vp); // must cast
int *np = NULL;
// *np = 5; // undefined behavior!
int factR(int n) { return n <= 1 ? 1 : n * factR(n-1); }
int factI(int n) {
int r = 1;
for (int i = 2; i <= n; i++) r *= i;
return r;
}
enum Direction { NORTH, SOUTH, EAST, WEST }; // 0,1,2,3
enum Status { OK=200, NOT_FOUND=404, ERROR=500 };
enum Direction d = NORTH;
char str[] = "Hello World";
memmove(str + 6, str, 5); // safe overlap
char dest[20];
memcpy(dest, str, strlen(str) + 1); // non-overlapping
char str[] = "Hello";
printf("%zu", sizeof(str)); // 6 (5 chars + null)
printf("%zu", strlen(str)); // 5 (without null)
int n = atoi("42"); // 42, no error check
char *end;
long l = strtol("42abc", &end, 10);
printf("%s", end); // "abc" - where conversion stopped
int x = 5, y = 10;
const int *p1 = &x; // *p1 = 6 is error; p1 = &y is OK
int * const p2 = &x; // p2 = &y is error; *p2 = 6 is OK
#define MAX(a,b) ((a)>(b)?(a):(b)) // macro
MAX(x++, y++); // x or y incremented twice!
static inline int max(int a, int b) { return a > b ? a : b; }
void infinite() { infinite(); } // stack overflow
char buf[10];
strcpy(buf, "This is way too long"); // buffer overflow!
int i = 10;
while (i < 5) { printf("never"); } // never executes
do { printf("once"); } while (i < 5); // executes once
for (int i = 0; i < 10; i++) {
if (i == 3) continue; // skip 3
if (i == 7) break; // stop at 7
printf("%d ", i); // 0 1 2 4 5 6
}
Arrays are always passed by reference in C - the function receives a pointer to the first element. Modifying the array inside the function modifies the original.
void modify(int arr[], int n) {
arr[0] = 99; // modifies original
}
void readOnly(const int arr[], int n) {
// arr[0] = 99; // compile error
}
int arr[10];
memset(arr, 0, sizeof(arr)); // zero all elements
memset(arr, -1, sizeof(arr)); // fill with 0xFF bytes
struct Padded { char c; int i; }; // sizeof = 8 (padding added)
struct __attribute__((packed)) Packed { char c; int i; }; // sizeof = 5
Explore 500+ free tutorials across 20+ languages and frameworks.