Course Topics
C Basics Introduction and Setup Syntax and Program Structure Comments and Documentation Compiling and Running C Programs Exercise Variables and Data Types Variables and Declaration Data Types (int, float, char, double) Constants and Literals Type Conversion and Casting Exercise Operators Arithmetic Operators Comparison Operators Logical Operators Assignment Operators Bitwise Operators Exercise Input and Output Standard Input/Output (scanf, printf) Format Specifiers File Input/Output Exercise Control Flow - Conditionals If Statements If-Else Statements Switch Statements Nested Conditionals Exercise Control Flow - Loops For Loops While Loops Do-While Loops Loop Control (break, continue) Nested Loops Exercise Functions Defining Functions Function Parameters and Arguments Return Statements Scope and Variables Recursion Exercise Arrays One-Dimensional Arrays Multi-Dimensional Arrays Array Operations Strings as Character Arrays Exercise Pointers Introduction to Pointers Pointer Arithmetic Pointers and Arrays Pointers and Functions Dynamic Memory Allocation Exercise Strings String Handling String Functions (strlen, strcpy, strcmp) String Manipulation Exercise Structures Defining Structures Structure Members Arrays of Structures Pointers to Structures Exercise File Handling Opening and Closing Files Reading from Files Writing to Files File Positioning Exercise Memory Management Static vs Dynamic Memory malloc() and free() Memory Leaks Best Practices Exercise Advanced Topics Preprocessor Directives Macros Header Files Modular Programming Exercise Final Project Project Planning Building Complete Application Code Organization Testing and Debugging Exercise

Testing and Debugging

Introduction

  • Understanding testing and debugging is essential for writing high-quality and efficient code in C.
  • This topic will teach you how to test your programs for correctness, and debug them when errors occur.

Core Concepts

Testing

What it is: A process of executing a software program with the intent of finding whether it satisfies specified requirements or not.

Why it matters: Testing helps ensure that our code works as intended, catching potential issues before they reach end-users.

Debugging

What it is: The process of locating and removing errors (bugs) from a computer program.

Why it matters: Debugging helps us find and fix the mistakes we make when writing code, making our programs run correctly.

Practical Examples

Testing

To test your C programs, you can use standard input/output functions like printf() and scanf(), or create custom functions to validate user inputs.

Example:

#include <stdio.h>

int add_numbers(int a, int b) {
    return a + b;
}

int main() {
    int num1 = 5;
    int num2 = 7;
    int result = add_numbers(num1, num2);
    printf("The sum of %d and %d is: %d\n", num1, num2, result);
    return 0;
}

Debugging

To debug a C program, you can use tools such as GDB (GNU Debugger) or Visual Studio Code's built-in debugger. Here's an example of using GDB:

#include <stdio.h>

int add_numbers(int a, int b) {
    return a + b;
}

int main() {
    int num1 = 5;
    int num2 = 0; // Intentionally setting this to zero to trigger an error
    int result = add_numbers(num1, num2);
    printf("The sum of %d and %d is: %d\n", num1, num2, result);
    return 0;
}

To debug this code using GDB, compile it first:

gcc -g main.c -o main

Then run the compiled program with GDB:

gdb ./main
(gdb) run

GDB will then pause execution at the point of error, allowing you to inspect variables and step through code to identify and fix the issue.

Common Issues and Solutions (CRITICAL SECTION)

Segmentation Fault (e.g., SIGSEGV)

What causes it: Attempting to access memory that has not been allocated or is outside the bounds of an array.

#include <stdio.h>
int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    printf("%d\n", arr[6]); // Segmentation fault occurs here
    return 0;
}

Error message:

Program received signal SIGSEGV: Segmentation fault - invalid memory reference.

Solution: Ensure that you're accessing memory within the proper bounds or using dynamic memory allocation functions such as malloc().

Why it happens: Accessing unallocated or out-of-bounds memory causes your program to read or write data where it shouldn't, which can lead to unexpected results and crashes.

How to prevent it: Use appropriate array indices, check bounds before accessing memory, and be mindful of dynamic memory allocation.

Division by Zero (0)

What causes it: Dividing a number by zero.

#include <stdio.h>
int main() {
    int num = 10;
    double result = num / 0.0; // This line will cause a division by zero error
    printf("The value of %d divided by 0 is: %f\n", num, result);
    return 0;
}

Error message:

Floating point exception (core dumped)

Solution: Ensure that the denominator is never zero before performing division.

Why it happens: Division by zero results in an undefined or infinite value, which can cause problems for your program's calculations and lead to errors.

How to prevent it: Check if the denominator is zero before performing division and handle such cases appropriately (e.g., returning an error message).

Best Practices

  • Use meaningful variable names for easy understanding of your code
  • Write tests for each function or module you create
  • Debug your code using a debugger to find and fix errors more efficiently
  • Keep your functions small and focused to improve readability and maintainability
  • Document your code to help others (and future you) understand what it does

Key Takeaways

  • Testing is essential for ensuring that our programs work as intended, while debugging helps us find and fix errors.
  • Common errors like segmentation faults and division by zero can be prevented with proper memory management and careful calculations.
  • Adopting best practices such as writing tests, using meaningful variable names, and documenting your code will make your life (and others') easier in the long run.
  • Continue learning about advanced testing techniques, debugging strategies, and coding best practices to become a proficient C programmer.