Data Structures
Lecture 3: Arrays, Errors
Arrays
Introduction
• Arrays
– Data structures containing elements of the same data type
– Can store both primitive data (int, char) and derived data types
(objects and structs)
– Stored in contiguous memory locations accessed using indices
– Static entity - same size throughout program
– In C++, arrays are objects
• To access an element, specify
Arrays – Array name and position number
arrayname[ position number ]
– First element at position 0
if there are n elements in array c:
c[ 0 ], c[ 1 ]…c[ n - 1 ]
• Array elements are like normal variables
c[ 0 ] = 3;
cout << c[ 0 ];
• Performing operations in subscript. If x = 3,
c[ 5 – 2 ] == c[ 3 ] == c[ x ]
Arrays
all elements of this array have the same name, c
c[0] -45
c[1] 6
c[2] 0
c[3] 72
c[4] 1543
c[5] -89
c[6] 0
c[7] 62
c[8] -3
c[9] 1
c[10] 6453
c[11] 78
Position number of the element
within array c
Declaring Arrays
• Declaring arrays - specify:
– Type of array
– Name
– Number of elements
– Examples:
int c[ 10 ];
float hi[ 3284 ];
• Declaring multiple arrays of same type
– Similar format as other variables
– Example
int b[ 100 ], x[ 27 ];
Examples Using Arrays
• Initializers
int n[ 5 ] = { 1, 2, 3, 4, 5 };
– If not enough initializers, rightmost elements become 0
int n[ 5 ] = { 0 }
Sets all the elements to 0
– If too many initializers, a syntax error is generated
• If size omitted, the initializers determine it
int n[] = { 1, 2, 3, 4, 5 };
– 5 initializers, therefore n is a 5 element array
#include <iostream>
#include <iomanip> Element Value
0 32
using namespace std; 1 27
2 64
3 18
4 95
int main() { 5 14
int n[ 10 ] = { 32, 27, 64, 18, 95, 14, 90, 70, 60, 37 }; 6 90
7 70
cout << "Element" << setw( 13 ) << "Value" << endl; 8 60
9 37
for ( int i = 0; i < 10; i++ )
cout << setw( 7 ) << i << setw( 13 ) << n[ i ]<<endl;
return 0;
}
Dynamic Arrays
Dynamic Array
• Dynamic arrays can be re-sized
• Allocated with the keyword new
int* arr1 = new int[10];
Dynamic Array – Assignment 1 –
Part 1
Your program will read data from a file and store it in an integer array.
Then it will let the user act on the data in the array:
1) Verify that a value exists in the array. The function accepts a value and if the value is present
the function returns the index where the number is present otherwise returns -1.
2) Modify a value in the array. The function accepts the index position in the array and the new
value. It updates the value in the array and returns the old value back to the user.
3) Add a new integer to the end of the array. The function accepts the new value and adds it to
the end of the array. The array will be resized if needed.
4) Erase a value. The function accepts the index in the array and removes the value at that
position from the array.
Resizing the array by doubling the size as needed is necessary.
Functions need access to the size & current number of values in the array
struct change {
int oldV;
int newV;
};
bool loadArray(int* &arr, int &size, int &count);
char showMenu();
int findValue(int* const arr, int size, int count,
int &value);
change modifyValue(int* arr, int size, int count);
void addValue(int* &arr, int &size, int &count);
int eraseValue(int* arr, int& size, int& count);
void printArray(int* const arr, int size, int
count);
class arrayClass{
Array private:
Class int* arr;
int size;
int count;
public:
arrayClass(int s) {
size = s;
count = 0;
arr = new int[size] {0};
}
int getSize() { return size; }
int getCount() { return count; }
int getValue(int pos);
void resize();
int findValue(int value);
void modifyValue(int pos, int value);
void addValue(int value);
int eraseValue(int pos);
void printArray();
};
Dynamic Arrays – findValue
Function
//PRE: Array is loaded with valid values, size is the current size of array
// count is the number of entries in the array, value has not been set
//POST: Array, size and count will not be updated
// value will be accepted from the user and the value returned to main
// function will return the position of the value in the array or
// -1 if it doesn't exist
int findValue(int* const arr, int size, int count, int &value);
int findValue(int* const arr, int const &size, int const &count, int &value);
int findValue(int value);
Dynamic Arrays – modifyValue
Function
//PRE: Array is loaded with valid values, size is the current size of array
// count is the number of entries in the array, old/new values have not been
set
//POST: size and count will not be updated
// position in array is entered by user and verified.
// Continues to prompt user for a valid array position
// prompts user for new value
// continues to prompt user for a numeric entry
// saves old & new values & returns structure
change modifyValue(int* const arr, int size, int count); struct change {
int oldValue;
int newValue;
void arrayClass:: modifyValue(int pos, int value); };
Dynamic Arrays – resizing
/*
if {(count + 1) > size){
calculate newSize
allocate a newArray with newSize
copy the elements from the oldArray to
newArray
delete oldArray
set oldArray = newArray
set size = newSize We could update the address of
the array
(as well as the size & count)
}
in the functions
loadArray & addValue
void addValue(int* &arr, int &size, int &count)
void arrayClass::addValue(int value)
Program Defects and
"Bugs"
Program Defects and
"Bugs"
• Defects that occur in software products after delivery
can produce undesirable and sometimes disastrous
results
• Testing is used to show that a program is correct, but
• it is difficult to determine how much testing needs to be
done
• testing cannot demonstrate the complete absence of
defects
• complete testing in some environments (missile control
software, nuclear power plant controls) is very difficult
Program Defects and
"Bugs" (cont.)
• The term "bug" is used to refer to a software defect, but
many professionals feel that the term "bug" trivializes
defects that can have serious consequences
• Debugging is a commonly used term for removing defects
• A debugger is a testing tool that helps find defects
• It is much easier to eliminate defects by careful design than
through testing
• You may encounter three kinds of defects or errors
• Syntax errors
• Run-time errors or exceptions
• Logic errors
Syntax Errors
• Syntax errors are mistakes in using the grammar (syntax) of the C++
language
• The C++ compiler will detect most syntax errors during compilation
• Some common syntax errors:
• Omitting or misplacing braces that bracket compound statements
• Invoking a member function that is not defined for the object to
which it is applied
• Not declaring a variable before using it
• Providing multiple declarations of a variable
• Not assigning a value to a local variable before referencing it
• Not returning a value from a function whose result type is not void
Syntax Errors (cont.)
• Unfortunately, the compiler may not detect syntax errors
that are the result of typographical errors
• Typographical errors, such as using = when you intended to
use ==, may be valid syntax, but will not produce the
intended result
• (In the case of = instead of ==, the result is assignment instead of
comparison)
• Because a syntactically correct program can contain errors,
careful review and testing are essential
Run-time Errors
• Run-time errors occur during program execution
• A run-time error occurs when the computer or the C++ run-time
library detects an operation that it knows to be incorrect
• Most run-time errors cause the operating system or run-time library
to issue an error message and halt-the program
• Different computers, operating systems, and compilers handle these
errors differently
Run-time Errors: Division
by Zero
• The C++ standard states that the result of division by
zero is undefined
• By stating "undefined," the standard does not require
the compiler or the run-time system to detect it (and
may vary by the computer, operating system, and
compiler)
Run-time Errors: Division by
Zero (cont.)
average = sum / count;
• Under the Windows operating system, if the value of
count is zero and this statement is executed, it will
cause a division-by-zero error
average = sum / count;
• Under the Linux operating system, you merely get the
message:
Floating point exception
Run-time Errors: Division by
Zero (cont.)
• You can guard against such a division with an if statement:
if (count == 0)
average = 0.0;
else
average = sum / count;
Array Index Out of
Bounds
• C++ does not check the validity of array subscripts
• Attempting to access or modify an array element using a
subscript that is negative or greater than or equal to the
array's length is also an undefined condition
• If we define the following array,
int scores[500];
• and we use the subscripted variable scores[i] with a value of i less
than zero or greater than 499, a location in memory outside of the array
will be accessed or modified
• If the location happens to be a location not accessible to the program an
error may be reported OR the program may execute, but may exhibit
strange behavior at some point possibly far removed from the location
of the error
Array Index Out of
Bounds (cont.)
• Incorrect loop control parameters can cause array-
index-out-of-bounds errors
• We need to carefully check the boundary values for
an index that is also a loop control variable
• A common error is using the array size as the upper
limit rather than the array size minus 1
Array Index Out of
Bounds (cont.)
for (int i = 0; i <= X_SIZE; i++)
x[i] = i * i;
• The loop above will cause an array-index-out-of-bounds error
during the last pass
• We need to replace the loop header with one of the following:
for (int i = 0; i < X_SIZE; i++)
for (int i = 0; i <= X_SIZE - 1; i++)
for (int i = 0; i != X_SIZE; i++)
Array Index Out of
Bounds (cont.)
• Parameters passed to a function can include the position
and new value of an element in an array
• An if statement can be used to validate that the index is in
bounds
• The function can return a bool value indicating whether or
not the value was changed
bool set_element_of_x (int index, int val) {
if (index >= 0 && index < X_SIZE) {
x[index] = val;
return true;
} else {
return false;
}
}
Array Index Out of
Bounds (cont.)
• Parameters passed to the main function can be accessed as
strings stored in array argv
• The number of arguments passed to main are stored in
variable argc
• If we write a main function that expects two parameters, we
need to pass both parameters (otherwise we will generate
an array-out-of-bounds error)
int main(int argc, char* argv[]) {
string input_file_name;
if (argc > 1)
input_file_name = argv[1]
else
input_file_name = "Phone.dat";
...
Null Pointer
• If you attempt to dereference a null pointer, the
operating system generally will issue an error
message and terminate the program
• Avoid this error by ensuring pointers are not null
before dereferencing them
Logic Errors
• A logic error occurs when the programmer or analyst
• makes a mistake in the design of a class
• makes a mistake in the design of a class function
• has implemented an algorithm incorrectly
• uses an incorrect algorithm
• The C++ code will compile and run, but will produce
incorrect results
• Most logic errors do not cause syntax or run-time
errors and, consequently, are difficult to find
Logic Errors (cont.)
• Test cases can help find logic errors
• Test cases allow the programmer to compare actual
program output with the expected results
• Software professionals must be diligent in detecting
and correcting logical errors
Logic Errors (cont.)
• The original Mars Lander spacecraft crashed because of inconsistent
calculations (feet versus meters)
• Billing programs have sent out bills for exorbitant amounts of money
• ATM programs and off-track betting programs have caused machine
owners to lose significant amounts of money
• Many popular operating systems have been released with bugs that have
allowed hackers to access computers easily and illicitly
• In the 1980s several patients died after software controlling therapeutic
radiation machines gave them massive overdoses
• In August 2004, two automobile recalls that could have led to fatal
accidents occurred because of software errors in control systems
• In 2024, the CrowdStrike Falcon update hobbled the Microsoft Windows
systems worldwide https://www.cigniti.com/blog/software-failures-inadequate-software-testing/
https://www.linkedin.com/pulse/biggest-software-failures-
history-chronological-journey-corrales-3jube/
https://www.cigniti.com/blog/software-failures-inadequate-
software-testing/
Exceptions
Ways to Indicate an
Error
• Examples of ways to indicate an error:
• Return a special value from a function
• Use a bool return value to indicate success or failure
• Set a global variable
• Print an error message
• Print an error message and exit the program
• Put an input or output stream in a fail state
• The first three options allow the user of a function to
respond to the error
Return a special value from a
function (1)
int getElementOfX1(int x[], int index) {
if (index >= 0 && index < X_SIZE)
return x[index];
else
return INT_MIN; int value = getElementOfX(x, i);
}
if (value > INT_MIN) {
• Verify the index passed
// do something with
IF it is valid, return the value x[index] value
ELSE return the minimum value of the integer ...
• Check the value returned from the function } else {
// recover from the error
...
}
// continue normal processing
Ways to Indicate an Error -
Exceptions
• An alternate way to indicate an error, especially if there
are several possible errors, is through the use of
exceptions
• Exceptions are used to signal that an error has occurred
• You can insert code in your program that throws an
exception when a particular kind of error occurs
• When an exception is thrown, the normal flow of
execution is interrupted
• An exception handler allows the user to catch or handle
the exception
• If there is no exception handler, then the program
terminates
The throw Statement
(cont.)
int getElementOfX3 (int index) {
if (index >= 0 && index < X_SIZE)
return x[index];
else
throw std::out_of_range(
"In get_element_of_x, the value of index is out of range");
}
• The exception object can be of any type—for example, a string literal (of type const
char*) or a class instance (std::out_of_range)
• The standard library include classes that can be used to indicate exceptions
• For this type of exception, control is passed to the exception handler if one is
available
Uncaught Exceptions
• When an exception occurs and is not caught, the
program stops and an error message is displayed
• The error message is compiler and operating system
dependent and generally not very informative
• The g++ compiler under Linux gives this message:
aborted
• The Microsoft .NET C++ compiler gives this message:
This application has requested the Runtime to
terminate it in an unusual way.
Please contact the application's support team for
more information.
Catching and Handling Exceptions
with try and catch Blocks
• To avoid uncaught exceptions you write a try block that
can throw an exception and follow it with a catch block
that catches the exception and handles it If all statements in the try
block execute without
try {
val = getElementOfX(10);
error, the exception
} handler (the catch block)
catch (std::out_of_range& ex) { is skipped
std::cerr << "Out of range exception occurred\n";
std::cerr << ex.what() << endl;
std::abort();
}
Catching and Handling Exceptions
with try and catch Blocks (cont.)
• To avoid uncaught exceptions you write a try block
that can throw an exception and follow it with a
catch block that catches the exception and handles it
try {
If an out_of_range
val = getElementOfX(10); exception is thrown, the
} exception handler
catch (std::out_of_range& ex) { executes
std::cerr << "Out of range exception occurred\
n";
std::cerr << ex.what() << endl;
std::abort();
}
Catching and Handling Exceptions
with try and catch Blocks
• To avoid uncaught exceptions you write a try block
that can throw an exception and follow it with a
catch block that catches the exception and handles it
The exception handler simply
try { displays an error message and then
val = getElementOfX(10); exits with an error indication
}
catch (std::out_of_range& ex) {
std::cerr << "Out of range exception occurred\
n";
std::cerr << ex.what() << endl;
std::abort();
}
Catching and Handling Exceptions
with try and catch Blocks (cont.)
The exception
• To avoid uncaught exceptions you object
write ais try
actually
block
thrown (created)
that can throw an exception inside
and follow the a
it with
function getElementOfX but is
catch block that catches the exception and handles it
handled by the catch block in the
try { caller of getElementOfX
val = get_element_of_x(10);
}
catch (std::out_of_range& ex) {
std::cerr << "Out of range exception occurred\
n";
std::cerr << ex.what() << endl;
std::abort();
}
Catching and Handling Exceptions with
try and catch Blocks (cont.)
• More than one catch block can follow a try block
• Each catch block handles a different kind of exception
• They are checked in the order in which they appear
try {
code that may throw an exception
}
catch (type-1 parameter-1) {
statements to process type-1
}
…
catch (type-n parameter-n) {
statements to process type-n
}
Catching and Handling Exceptions with
try and catch Blocks (cont.)
• Although in our example, the catch block handles the
exception, it basically duplicates the default behavior for
uncaught exceptions with a slightly more informative error
message
• Next, we will learn how to use the catch block to recover
from errors and continue execution of a program
Standard
Exception
s (cont.)
Standard Exceptions
(cont.)
• Sample dialogues
Type in a string: unnecessary roughness
Enter start position of a substring: 2
The substring is necessary roughness
Type in a string: unnecessary roughness
Enter start position of a substring: 30
*** Out_of_range exception thrown for start position 30
basic_string::substr
Catching All Exceptions
• Although all exceptions should be derived from the
std:exception class, there is no guarantee —
exceptions may be of any type
• Even though we include an exception handler for
std:exception we might still have an uncaught
exception
• C++ allows us to catch all exceptions by using ... as
the exception handler parameter
• Unfortunately, C++ does not provide a way to
determine what exception was caught by the ...
parameter
Catching All Exceptions
(cont.) try {
age = read_int("Enter your age: ");
}
catch (std::ios_base::failure& f) {
cerr << "Invalid number format input\n";
age = DEFAULT_AGE;
}
catch (std::exception& ex ex) {
cerr << "Fatal error occurred in read_int\n";
cerr << ex.what() << endl;
abort();
}
catch (...) {
cerr << "Undefined exception occurred in read_int\n";
abort();
}
Handling Input Errors (1)
• A common source of errors is user input
• Instead of aborting, we can recover from the error
• We can use the following sequence:
if (cin >> i) {
// input of i was successful – process i
} else {
// problem reading i – attempt to recover
}
• However, this requires us to test the state of cin
after each input operation
Handling Input Errors (2)
int intValue;
try {
cout << "Enter a number: ";
cin >> intValue;
cout << intValue << endl;
}
catch (...) { abc is still
in the
cerr << "An error occurred\n"; buffer
}
if (cin.fail()) {
cout << "The failbit was set,but this doesn't
throw an exception\n";
}
Handling Input Errors (3)
int intValue;
cout << "Enter a number: ";
cin >> intValue;
while (cin.fail()) {
cin.clear();
cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n’);
cout << "Enter a VALID number: ";
cin >> intValue;
}
cout << "Thank you for entering the value: " << intValue << endl;
Handling Input Errors (4)
string inRec;
int intValue;
cout << "Enter a number: ";
getline(cin,inRec);
while (!isDigits(inRec)) {
cout << "Enter a VALID number: ";
getline(cin, inRec);
}
intValue = stoi(inRec);
cout << "Thank you for entering the value: " << intValue << endl;
Standard errors
• import stdexcept
• Logic Errors
• logic_error
• domain_error
• invalid_argument
• length_error
• out_of_range
• Runtime Errors
• runtime_error
• range_error
• overflow_error
• underflow_error