Dynamic Arrays in C: Partially Filled Arrays and Dynamic Allocation, Papers of Data Structures and Algorithms

The concept of dynamic arrays in c programming. It explains the use of partially filled arrays and their limitations, and presents a solution using dynamically allocated arrays. The document also covers essential c language features such as malloc, free, assert, and defensive programming.

Typology: Papers

Pre 2010

Uploaded on 08/30/2009

koofers-user-ftn-1
koofers-user-ftn-1 🇺🇸

8 documents

1 / 5

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
Worksheet 14: Introduction to Dynamic Arrays Name:
An Active Learning approach to Data Structures using C
1
Worksheet 14: Introduction to the Dynamic Array
In Preparation: Read Chapter 5 to learn more about abstraction and the basic abstract
data types, and the dynamic array as a n implementation technique.
Suppose you want to write a program that will read a list of numbers from a user, place
them into an array and then compute their average. How large should you make the
array? It must be large enough to hold all the values, but how many numbers will the user
enter? A common solution to this difficulty is to create an array that is larger than
necessary, and then only use the initial portion. This is termed a partially filled array.
When you use a partially filled array there are two integer values of interest. First is the
array length. When discussing partially filled array this
is sometimes termed the capacity of the array. Second is
the current size, that is, the amount of the array that is
currently being used. The size is generally maintained
by a separate integer variable. If we place the values into
a structure it will be easier to keep them together.
It is important to not confuse the size and the capacity. For example, in computing the
sum and average you do not want to use the capacity:
double average (struct partFillArray * pdata) {
double sum = 0.0;
int i;
for (i = 0; i < 50; i++) /* Error–loop using length */
sum = sum + pdata->data[i]; /* Error-uninitialized value */
return sum / 50; /* Error–average is under estimated */
}
Instead, you want to use the size:
double average (struct partFillArray * pdata) {
double sum = 0.0;
int i;
for (i = 0; i < pdata->size; i++)
sum = sum + pdata->data[i];
return sum / pdata->size;
}
struct partFillArray {
double data[50];
int size;
};
pf3
pf4
pf5

Partial preview of the text

Download Dynamic Arrays in C: Partially Filled Arrays and Dynamic Allocation and more Papers Data Structures and Algorithms in PDF only on Docsity!

Worksheet 14: Introduction to the Dynamic Array

In Preparation : Read Chapter 5 to learn more about abstraction and the basic abstract

data types, and the dynamic array as a n implementation technique.

Suppose you want to write a program that will read a list of numbers from a user, place

them into an array and then compute their average. How large should you make the

array? It must be large enough to hold all the values, but how many numbers will the user

enter? A common solution to this difficulty is to create an array that is larger than

necessary, and then only use the initial portion. This is termed a partially filled array.

When you use a partially filled array there are two integer values of interest. First is the

array length. When discussing partially filled array this

is sometimes termed the capacity of the array. Second is

the current size , that is, the amount of the array that is

currently being used. The size is generally maintained

by a separate integer variable. If we place the values into

a structure it will be easier to keep them together.

It is important to not confuse the size and the capacity. For example, in computing the

sum and average you do not want to use the capacity:

double average (struct partFillArray * pdata) { double sum = 0.0; int i; for (i = 0; i < 50; i++) /* Error–loop using length / sum = sum + pdata->data[i]; / Error-uninitialized value / return sum / 50; / Error–average is under estimated */ }

Instead, you want to use the size:

double average (struct partFillArray * pdata) { double sum = 0.0; int i; for (i = 0; i < pdata->size; i++) sum = sum + pdata->data[i]; return sum / pdata->size; } struct partFillArray { double data[50]; int size; };

The technique of partially filled arrays works fine until the first time that the user enters

more numbers than were originally anticipated. When this happens, the size can exceed

the capacity, and unless some remedial action is taken an array indexing error will occur.

Worse yet, since the validity of index values is not checked in C, this error will not be

reported and may not be noticed.

A common solution to this problem is to use a pointer to a dynamically allocated array,

rather than a fixed length array. Of course, this means that the array must be allocated

before it can be used. We can write an initialization routine for this purpose, and a

matching routine to free the data. Next, we likewise encapsulate the action of adding a

new element into a function. This function can check that the size does not exceed the

capacity, and if it does increase the length of the array (generally by doubling) and

copying all the elements into the new area. Now the user can enter any number of values

and the data array will be automatically expanded as needed.

ifndef EleType

define EleType double

endif

struct dyArray { EleType * data; int size; int capacity; }; void dyArrayInit (struct dyArray * da, int iCap) { da->capacity = iCap; assert (iCap > 0); da->size = 0; da->data = (EleType *) malloc(da->capacity * sizeof(EleType)); assert (da->data != 0); } void dyArrayFree (struct dyArray * da) { free(da->data); da->data = 0; da->capacity = 0; da->size = 0; } int dyArraySize (struct dyArray * da) { return da->size; } void dyArrayAdd (struct dyArray * da, EleType newValue) { if (da->size >= da->capacity) _dyArrayDoubleCapacity(da); da->data[da->size] = newValue; da->size += 1; }

of elements you need by the size of each element, and you have a block of memory that

can be used as an array.

assert. The malloc function will return zero if there is insufficient memory to satisfy a

request. The assert macro will halt execution with an error message if its argument

expression is not true. Assertions can be used any time a condition must be satisfied in

order to continue.

free. The function free is the opposite of malloc. It is used to return a block of memory

to the free store. Such memory might later be reused to satisfy a subsequent malloc

request. You should never use malloc without knowing where and when the memory

will subsequently be freed.

Defensive programming. When the memory is released in the function dyArrayFree the

size and the capacity are both set to zero. This ensures that if a subsequent attempt is

made to insert a value into the container, there will not be an attempt to index into an

already deleted array. Another example of defensive programming occurs in the function

_dyArrayDoubleCapacity. What happens if the current capacity is zero when this

function is called? One response would be to use assert, and halt execution if this is true.

Another approach, taken here, is to make the function do something reasonable in this

situation, namely to create an array with a capacity of two elements.

dyArraySize. Since the size field is stored as part of the dynamic array structure there

really is no need for this function, since the user can always access the field directly.

However, this function helps preserve encapsulation. The end user for our container need

not understand the structure definition, only the functions needed to manipulate the

collection.

_dyArrayDoubleCapacity. An underscore is treated as a legal letter in the C language

definition for the purposes of forming identifiers. There is a common convention in the C

programming community that function names beginning with an underscore are used

“internally”, and should never be directly invoked by the end user. We will follow that

convention in our code. The function _dyArrayDoubleCapacity is can be called by

dynamic array functions, but not elsewhere.

Note carefully the order of operations in the function _dyArrayDoubleCapacity. First,

the new array is created. Next, the old values are copied into the new array. The free

statement then released the old memory. Finally, the pointer is changed to reference the

new array.

In order to allow a dynamically allocated array to be used in the same fashion as a normal

array, we need functions that will get and set values at a given position. We can also

make our function more robust than the regular C array by checking index positions.

Complete the implementation of the following functions. Use assert to check that index

positions are legal.

EleType dyArrayGet (struct dyArray * da, int position) { } void dyArrayPut (struct dyArray * da, int position, EleType value) { }

Write the function swap, which will exchange the values in two positions of a dynamic

array. We will use this function in later chapters.

void dyArraySwap (struct dyArray * da, int i, int j) { }