0% found this document useful (0 votes)
3 views

CMP452-Structured Programming-1

The document covers structured programming concepts in C++ focusing on debugging, testing, and program verification over three weeks. It details types of errors in programming, debugging techniques, the importance of testing, various testing types, and approaches to program verification. Additionally, it discusses test case design and verification methods, including manual and formal verification, and the use of assertions and loop invariants in ensuring program correctness.

Uploaded by

kheentab121
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
3 views

CMP452-Structured Programming-1

The document covers structured programming concepts in C++ focusing on debugging, testing, and program verification over three weeks. It details types of errors in programming, debugging techniques, the importance of testing, various testing types, and approaches to program verification. Additionally, it discusses test case design and verification methods, including manual and formal verification, and the use of assertions and loop invariants in ensuring program correctness.

Uploaded by

kheentab121
Copyright
© © All Rights Reserved
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 8

CMP452: Structured Programming (C++)

Weeks 7-9: Debugging, Testing, and Program Verification


Instructor: Mr. Mukaddas

Week 7: Language for Structured Programming Debugging and Code Inspection

Debugging is an essential process in programming that involves identifying and fixing errors
in code. Errors in programming can be classified into three main categories:

1. Syntax Errors: Errors in the structure or grammar of the code. The compiler detects
these.
o Example: Missing semicolon, misspelled keyword.
2. Semantic Errors: Errors in the meaning of the code. The compiler may not catch
these, but the program behaves incorrectly.
o Example: Using an uninitialized variable, incorrect operator precedence.
3. Logical Errors: The program compiles and runs, but it doesn't produce the desired
output due to a flaw in the algorithm.
o Example: Incorrect loop condition, wrong formula.
4. Runtime Errors: Errors that occur while the program is running. These can cause the
program to crash.
o Example: Division by zero, accessing an invalid memory location (e.g., out-
of-bounds array access).
Code example for all errors :
#include <iostream>

int main() {
// Syntax Error: Missing semicolon
int x = 10 // Missing semicolon

// Semantic Error: Invalid operation (assigning a string to an integer)


int y = "hello"; // Type mismatch

// Logical Error: Incorrect loop condition (Infinite loop)


for (int i = 0; i < 10; i--) { // i decreases, never reaches 10
std::cout << i << std::endl;
}

// Runtime Error: Division by zero


int a = 10, b = 0;
int result = a / b; // Causes runtime crash
return 0;
}

Debugging Techniques in C++:


1. Using a Debugger:

 Introduction to the GNU Debugger (gdb): steps involved;


 Setting breakpoints: Pause program execution at a specific line.
 Stepping through code: Execute code line by line (step over, step into, step
out).
 Inspecting variables: Examine the values of variables at any point.
 Watching variables: Monitor how the value of a variable changes.
 Backtraces: See the call stack to understand the sequence of function calls.
 IDE Debuggers: Using debuggers within Integrated Development Environments
(IDEs) like Code:Blocks, Visual Studio Code (with the C++ extension), or CLion.
These provide a more user-friendly interface to gdb.
2. Print Statement Debugging (Debugging with std::cout):

Inserting std::cout statements to display the values of variables or trace the flow of
execution.

Example:

 #include <iostream>
 int main() {
 int x = 10;
 std::cout << "Value of x before loop: " << x << std::endl;
 for (int i = 0; i < 5; ++i) {
 x += i;
 std::cout << "Value of x in loop (i=" << i << "): " << x << std::endl;
 }
 std::cout << "Value of x after loop: " << x << std::endl;
 return 0;
 }

Code Inspection:

Code inspection is a systematic examination of code to identify errors, maintainability issues,


and adherence to coding standards.

Code inspection involves manually reviewing code for errors before execution.

Purpose of Code Inspection:


 Identify defects early in the development process, before testing.
 Improve code readability and maintainability.
 Ensure adherence to coding standards.
 Promote knowledge sharing among developers.
Code Inspection Techniques:
 Checklists: Use predefined lists of common errors or coding guidelines to
systematically review code.
o Example checklist items:
 Are all variables initialized before use?
 Is loop conditions, correct?
 Are resources (e.g., memory) properly managed?
 Is the code well-commented?
 Walkthroughs: The developer leads a team through the code, explaining its logic.
Team members ask questions and identify potential issues.
 Formal Inspections: A more structured process with defined roles (moderator, reader,
recorder, etc.) and specific steps.

C++-Specific Code Inspection Considerations:


 Memory management (new/delete, RAII).
 Pointer usage (null pointer checks, avoiding memory leaks).
 Object-oriented programming principles (correct use of inheritance, polymorphism).
 Use of the Standard Template Library (STL) (correct use of containers and
algorithms).

Example (Debugging with gdb):


#include <iostream>

int sum(int a, int b) {


int result; // Uninitialized variable
result = a + b;
return result;
}
int main() {
int x = 5;
int y = 10;
int total = sum(x, y);
std::cout << "Sum: " << total << std::endl;
return 0;
}

Debugging this with gdb:


1. Compile with debug information: g++ -g main.cpp -o main
2. Start gdb: gdb ./main
3. Set a breakpoint in the sum function: break sum
4. Run the program: run
5. Print the value of result: print result (You'll see it's uninitialized)
6. Step to the next line: next
7. Print the value of result again: print result (Now it has the correct sum)
8. Continue execution: continue

Week 8: Test Construction, Generation, and Running

Importance of Testing: Testing ensures that a program behaves as expected.

 Why is testing necessary?


o To find defects before the software is released to users.
o To ensure that the software meets its requirements.
o To improve the reliability and quality of the software.
o To reduce the cost of fixing bugs (it's cheaper to fix them early).
 The cost of not testing
o Financial losses
o Damage to reputation
o Safety-critical failures

Types of Software Testing:


 Unit Testing: Testing individual components or functions in isolation.
o Focuses on verifying that each unit of code works as expected.
 Integration Testing: Testing the interaction between different units or modules.
o Focuses on verifying that the units work together correctly.
 System Testing: Testing the complete system as a whole.
o Focuses on verifying that the system meets all of its requirements.
 Acceptance Testing: Testing the system from the user's perspective to determine if it's
acceptable.
o Often performed by the customer or end-users.
 Regression Testing: Retesting previously tested parts of the software after changes
have been made.
o Ensures that new changes haven't introduced new bugs or broken existing
functionality.
Other Types of Testing:

 Black-box testing: Testing based on the specification, without knowledge of the


internal code.
 White-box testing: Testing based on the internal structure of the code.
 Performance testing: Testing the speed, responsiveness, and stability of the system.
 Security testing: Testing the system's vulnerability to security threats.

Test Case Design:


 What is a Test Case?
o A specific set of inputs, execution conditions, and expected outputs designed
to test a particular aspect of a program.
 Test Case Components:
o Test case ID
o Test case description
o Preconditions
o Input data
o Steps to execute
o Expected output
o Postconditions
 Test Case Design Techniques:
o Black-Box Techniques:
 Equivalence Partitioning: Divide the input domain into groups
(partitions) where the program is expected to behave similarly. Test
one value from each partition.
 Boundary Value Analysis: Test values at the boundaries of the input
partitions (minimum, maximum, just inside, just outside).
 Decision Table Testing: Create a table to represent complex logic and
test all possible combinations of conditions.
o White-Box Techniques:
 Statement Coverage: Design tests to execute every statement in the
code at least once.
 Branch Coverage: Design tests to execute every branch (e.g., if/else
paths) in the code at least once.
 Path Coverage: Design tests to execute every possible path through the
code (impractical for complex programs).

 Test Automation with C++ and a Testing Framework:


o Why Automate Tests?
 Saves time and effort.
 Improves test coverage.
 Enables frequent regression testing.
 Increases confidence in the code.
o Introduction to a C++ Testing Framework (Catch2):
 Catch2 is a popular open-source testing framework for C++.
 It provides macros for defining test cases and assertions.
 It simplifies the process of writing and running tests.

Basic Catch2 Example:

o #define CATCH_CONFIG_MAIN // This tells Catch to provide a main() function


o #include "catch.hpp"
o
o int add(int a, int b) {
o return a + b;
o }
o TEST_CASE("Test addition function", "[add]") {
o REQUIRE(add(2, 3) == 5);
o REQUIRE(add(-1, 1) == 0);
o REQUIRE(add(0, 0) == 0);
o }

#define CATCH_CONFIG_MAIN: Only in one .cpp file.


#include "catch.hpp": Include the Catch2 header.
TEST_CASE: Defines a test case with a name and optional tags.
REQUIRE: An assertion that checks if a condition is true. If the condition is false, the test
fails.

Example (Test Case Design - Equivalence Partitioning):


Function: int divide(int a, int b)
Input: a (integer), b (integer)
Requirements:
 Returns the result of dividing a by b.
 If b is 0, throws an exception.
Equivalence Partitions:
 Partition for a: Any integer (positive, negative, zero).
 Partition for b:
o Positive integer (e.g., 1, 2, 10)
o Negative integer (e.g., -1, -2, -10)
o Zero (0)

Test Cases:

| Test Case ID | Description | a | b | Expected Output/Behavior |

| TC-01 | Positive a, positive b | 10 | 2 | 5 |

| TC-02 | Negative a, positive b | -10 | 2 | -5 |

| TC-03 | Zero a, positive b | 0 | 2 | 0 |

| TC-04 | Positive a, negative b | 10 | -2 | -5 |

| TC-05 | Negative a, negative b | -10 | -2 | 5 |

| TC-06 | Zero a, negative b | 0 | -2 | 0 |

| TC-07 | Positive a, zero b | 10 | 0 | Throws exception |

| TC-08 | Negative a, zero b | -10 | 0 | Throws exception |

| TC-09 | Zero a, zero b | 0 | 0 | Throws exception |

Week 9: Programme Verification

What is Program Verification? Program verification is ensuring that a program meets its
specifications and works correctly under all conditions.

 The process of formally proving that a program meets its specification.


 Goes beyond testing; aims to provide mathematical certainty about program
correctness.
 Complementary to testing: Verification can prove correctness for all possible inputs,
while testing can only check a finite set of inputs.
Approaches to Program Verification:
 Testing vs. Verification
o Testing: Shows the presence of bugs but cannot guarantee their absence.
o Verification: Proves the absence of bugs, within the scope of the specification.

Types of Verification

 Manual Verification:
o Proof by hand: Using mathematical logic to reason about the program's
behavior.
o Code Reviews: Can also be considered a form of manual verification,
especially when reviewers are looking for logical errors.
 Formal Verification:
o Using mathematical logic and automated tools to verify program correctness.
o Theorem Proving: Expressing program properties as theorems and using a
theorem prover to prove them.
o Model Checking: Checking if a program satisfies a given specification by
systematically exploring all possible states.
o Abstract Interpretation: Analyzing the program's behavior by considering an
abstract version of its data and operations.

 Verification by Assertion (Using assert in C++):

o The assert macro in C++ can be used to check if a condition is true at a


particular point in the program.
o If the condition is false, the program terminates with an error message.
o Can be used to verify preconditions, postconditions, and loop invariants.
o Example:
o #include <iostream>
o #include <cassert>
o
o int divide(int a, int b) {
o assert(b != 0); // Precondition: b must not be zero
o return a / b;
o }
o
o int main() {
o int x = 10;
o int y = 2;
o int result = divide(x, y);
o std::cout << "Result: " << result << std::endl;
o
o int z = 5;
o int w = 0;
o // int result2 = divide(z, w); // This will cause an assertion failure
o return 0;
o }
o

Assertions are typically enabled during development and testing, and disabled in production
code (by defining the macro NDEBUG).

Loop Invariants:
 A loop invariant is a condition that is true before the loop starts, remains true after
each iteration of the loop, and is true when the loop terminates.
 Loop invariants are crucial for verifying the correctness of loops.
 Steps to verify a loop using a loop invariant:
o Initialization: Show that the invariant is true before the loop starts.
o Maintenance: Show that if the invariant is true at the beginning of an iteration,
it is also true at the end of the iteration.
o Termination: Show that when the loop terminates, the invariant and the loop
termination condition imply the desired result.

You might also like