Skip to main content

C Common Mistakes

This guide covers common C programming mistakes and how to avoid them, with practical solutions and examples.

🚨 Most Common C Errors

1. Memory Leaks

Forgetting to free allocated memory or using freed memory.

Problem

// Bad: Memory leak
#include <stdlib.h>
#include <stdio.h>

void memory_leak_example() {
int* ptr = (int*)malloc(sizeof(int) * 100);
ptr[0] = 42;
// Forgot to free ptr!
}

void use_freed_memory() {
int* ptr = (int*)malloc(sizeof(int));
*ptr = 42;
free(ptr);
printf("%d\n", *ptr); // Using freed memory!
}

Solutions

// Good: Proper memory management
#include <stdlib.h>
#include <stdio.h>

void proper_memory_management() {
int* ptr = (int*)malloc(sizeof(int) * 100);
if (ptr == NULL) {
fprintf(stderr, "Memory allocation failed\n");
return;
}

ptr[0] = 42;
printf("%d\n", ptr[0]);
free(ptr); // Always free allocated memory
ptr = NULL; // Avoid dangling pointers
}

// Better: Use RAII-style pattern
typedef struct {
int* data;
size_t size;
} SafeArray;

SafeArray* create_safe_array(size_t size) {
SafeArray* arr = (SafeArray*)malloc(sizeof(SafeArray));
if (arr == NULL) return NULL;

arr->data = (int*)malloc(size * sizeof(int));
if (arr->data == NULL) {
free(arr);
return NULL;
}

arr->size = size;
return arr;
}

void destroy_safe_array(SafeArray* arr) {
if (arr != NULL) {
free(arr->data);
free(arr);
}
}

2. Buffer Overflow

Writing beyond array bounds or using unsafe string functions.

Problem

// Bad: Buffer overflow vulnerability
#include <string.h>
#include <stdio.h>

void buffer_overflow() {
char buffer[10];
strcpy(buffer, "This string is too long"); // Overflow!
printf("%s\n", buffer);
}

// Bad: Array bounds violation
void array_bounds_violation() {
int arr[5] = {1, 2, 3, 4, 5};
for (int i = 0; i <= 5; i++) { // Should be i < 5
printf("%d\n", arr[i]); // Reads beyond array!
}
}

Solutions

// Good: Safe string operations
#include <string.h>
#include <stdio.h>

void safe_string_operations() {
char buffer[10];
strncpy(buffer, "This string is too long", sizeof(buffer) - 1);
buffer[sizeof(buffer) - 1] = '\0'; // Ensure null termination
printf("%s\n", buffer);
}

// Good: Proper array bounds
void safe_array_access() {
int arr[5] = {1, 2, 3, 4, 5};
int size = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < size; i++) { // Correct bounds
printf("%d\n", arr[i]);
}
}

3. Uninitialized Variables

Using variables before initialization leads to undefined behavior.

Problem

// Bad: Uninitialized variables
int uninitialized_example() {
int x, y, z;
printf("%d\n", x + y + z); // Undefined behavior!

int* ptr;
*ptr = 42; // Writing to uninitialized pointer!
}

Solutions

// Good: Always initialize variables
int initialized_example() {
int x = 0, y = 0, z = 0;
printf("%d\n", x + y + z);

int* ptr = NULL;
if (ptr != NULL) {
*ptr = 42;
}
}

// Better: Use compiler warnings
// Compile with: gcc -Wall -Wextra -Werror

4. Integer Overflow

Arithmetic operations exceeding the range of data types.

Problem

// Bad: Integer overflow
#include <limits.h>
#include <stdio.h>

void integer_overflow() {
int max_int = INT_MAX;
int result = max_int + 1; // Overflow!
printf("%d\n", result); // Undefined behavior
}

Solutions

// Good: Check for overflow
#include <limits.h>
#include <stdio.h>

void safe_integer_operations() {
int max_int = INT_MAX;
int result;

// Check before addition
if (max_int > INT_MAX - 1) {
printf("Would overflow\n");
result = INT_MAX;
} else {
result = max_int + 1;
}

printf("%d\n", result);
}

// Better: Use larger data types
void safe_with_larger_type() {
long long max_int = INT_MAX;
long long result = max_int + 1; // No overflow
printf("%lld\n", result);
}

5. Pointer Errors

Incorrect pointer usage leading to crashes or undefined behavior.

Problem

// Bad: Pointer errors
#include <stdio.h>
#include <stdlib.h>

void pointer_errors() {
int* ptr = (int*)malloc(sizeof(int));
*ptr = 42;

// Error 1: Dereferencing NULL pointer
int* null_ptr = NULL;
printf("%d\n", *null_ptr); // Crash!

// Error 2: Returning pointer to local variable
int local_var = 100;
return &local_var; // Dangling pointer!
}

Solutions

// Good: Safe pointer usage
#include <stdio.h>
#include <stdlib.h>

int* safe_pointer_usage() {
int* ptr = (int*)malloc(sizeof(int));
if (ptr == NULL) {
fprintf(stderr, "Memory allocation failed\n");
return NULL;
}

*ptr = 42;

// Check pointer before dereferencing
int* null_ptr = NULL;
if (null_ptr != NULL) {
printf("%d\n", *null_ptr);
}

return ptr; // Return allocated memory, not local variable address
}

// Better: Use static allocation for returning pointers
int* get_static_pointer() {
static int static_var = 100;
return &static_var; // Safe: static variable exists after function returns
}

🔧 Logic Errors

6. Off-by-One Errors

Incorrect loop boundaries or array indexing.

Problem

// Bad: Off-by-one error
void off_by_one_error() {
int arr[5] = {1, 2, 3, 4, 5};

// Process first 5 elements
for (int i = 0; i <= 5; i++) { // Should be i < 5
printf("%d\n", arr[i]); // Accesses arr[5]!
}
}

Solutions

// Good: Correct loop bounds
void correct_loop_bounds() {
int arr[5] = {1, 2, 3, 4, 5};

// Process first 5 elements
for (int i = 0; i < 5; i++) { // Correct bounds
printf("%d\n", arr[i]);
}
}

// Better: Use sizeof for array size
void better_loop_bounds() {
int arr[5] = {1, 2, 3, 4, 5};
int size = sizeof(arr) / sizeof(arr[0]);

for (int i = 0; i < size; i++) {
printf("%d\n", arr[i]);
}
}

7. Infinite Loops

Loops that never terminate due to incorrect conditions.

Problem

// Bad: Infinite loop
void infinite_loop() {
int i = 0;
while (i < 10) {
printf("%d\n", i);
// Forgot i++ - infinite loop!
}
}

Solutions

// Good: Ensure loop termination
void correct_loop() {
int i = 0;
while (i < 10) {
printf("%d\n", i);
i++; // Critical!
}
}

// Better: Use for-loop when possible
void better_loop() {
for (int i = 0; i < 10; i++) {
printf("%d\n", i);
}
}

8. Incorrect Format Specifiers

Using wrong printf format specifiers.

Problem

// Bad: Format specifier mismatch
#include <stdio.h>

void format_errors() {
int num = 42;
double pi = 3.14159;

printf("%d\n", pi); // Wrong: %d for double
printf("%f\n", num); // Wrong: %f for int
printf("%s\n", num); // Wrong: %s for int
}

Solutions

// Good: Correct format specifiers
#include <stdio.h>

void correct_format() {
int num = 42;
double pi = 3.14159;

printf("%d\n", num); // Correct: %d for int
printf("%f\n", pi); // Correct: %f for double
printf("%d\n", num); // Correct: %d for int
}

9. Type Conversion Issues

Implicit type conversions causing unexpected behavior.

Problem

// Bad: Implicit conversion issues
#include <stdio.h>

void type_conversion_errors() {
int a = 5;
int b = 2;
double result = a / b; // Integer division, then convert to double
printf("%f\n", result); // Prints 2.000000, not 2.500000
}

Solutions

// Good: Explicit type conversion
#include <stdio.h>

void correct_type_conversion() {
int a = 5;
int b = 2;
double result = (double)a / b; // Explicit conversion
printf("%f\n", result); // Prints 2.500000
}

// Better: Use appropriate literals
void better_type_conversion() {
int a = 5;
int b = 2;
double result = a / 2.0; // Double literal forces floating point division
printf("%f\n", result); // Prints 2.500000
}

🛠️ Debugging Tips

Common Debugging Strategies

  1. Use Compiler Warnings: Enable all warnings with -Wall -Wextra
  2. Use Debugger: GDB for runtime debugging
  3. Memory Tools: Valgrind for memory issues
  4. Static Analysis: Tools like cppcheck for code analysis
  5. Assert Usage: Use assertions for development-time checks

Debugging Checklist

  • Initialize all variables
  • Check array bounds before access
  • Validate pointers before dereferencing
  • Check malloc return values
  • Free all allocated memory
  • Use correct format specifiers
  • Check for integer overflow
  • Ensure loop termination

Using GDB Effectively

# Compile with debug symbols
gcc -g program.c -o program

# Common GDB commands
(gdb) break main # Set breakpoint
(gdb) run # Start program
(gdb) next # Next line
(gdb) step # Step into function
(gdb) print variable # Print variable value
(gdb) continue # Continue execution
(gdb) bt # Show backtrace
(gdb) quit # Exit debugger

Memory Debugging with Valgrind

# Compile with debug symbols
gcc -g program.c -o program

# Run with valgrind
valgrind --leak-check=full --show-leak-kinds=all ./program

# Common valgrind options
--leak-check=full # Detailed leak information
--show-leak-kinds=all # Show all leak types
--track-origins=yes # Track where uninitialized values come from

🔗 Common Programming Mistakes

📍 Visit Us

🏫 VD Computer Tuition Surat

VD Computer Tuition
📍 Address
2/66 Faram Street, Rustompura
Surat395002, Gujarat, India
📞 Phone / WhatsApp
+91 84604 41384
🌐 Website

Computer Classes & Tuition — Areas We Serve in Surat

AdajanAlthanAmroliAthwaAthwalinesBhagalBhatarBhestanCanal RoadChowkCitylightDumasGaurav PathGhod Dod RoadHaziraJahangirpuraKamrejKapodraKatargamLimbayatMagdallaMajura GateMota VarachhaNanpuraNew CitylightOlpadPalPandesaraParle PointPiplodPunaRanderRing RoadRustampuraSachinSalabatpuraSarthanaSosyo CircleUdhnaVarachhaVed RoadVesuVIP Road
📞 Call Sir💬 WhatsApp Sir