Description
The purpose of this lab assignment is to get more practice programming in C, including the character functions
in the library ctype.h, and dynamic memory allocation using malloc, calloc, and free.
The Character Library
The C standard library ctype.h contains many functions for classifying and handling character data. For
historical reasons the arguments to these functions are of type int rather than char. In order to avoid a
compiler warning (under gcc –std=c99 –Wall), it is necessary to first cast the char argument as int. For
instance, ctype.h contains the function:
int isalnum(int ch);
which returns non-zero (true) if ch is an alphanumeric character (i.e. a letter or a digit), and 0 (false) if ch is
any other type of character. The following C program reads any number of strings from the command line and
classifies each character as either alphanumeric, or non-alphanumeric.
#include
#include
#include
#include
int main(int argc, char* argv[]){
char ch;
int i, j, count;
if( argc>1 )
{
for(i=1; i
Abort
You can verify this by running ex3.c on the examples page. As previously mentioned, heap memory is deallocated by the free function.
free(p);
Note that all this instruction does is convert the block of heap memory pointed to by p from allocated to free.
The local variable p still stores the address of this block, and therefore it is still possible to dereference p and
alter the contents of the block. Never do this! As previously mentioned, such an operation could lead to
runtime errors that are intermittent and very difficult to trace. Instead, after calling free, set the pointer safely
to NULL.
free(p);
p = NULL;
Now any attempt to follow the pointer p will result in a segmentation fault, which although it is a runtime
error, will happen consistently and can be traced more easily. Another common error occurs when one
reassigns a pointer without first freeing the memory it points to. Consider the following instructions.
int* p;
p = malloc(sizeof(int));
*p = 6;
p = malloc(sizeof(int));
*p = 7;
Observe that the address of the block of heap memory storing the value 6 is lost. The address cannot be
assigned to another pointer variable. The block cannot be de-allocated and therefore cannot be re-allocated at
any future time. The block storing the value 6 is therefore completely lost to the program and can be of no
further use. This situation is called a memory leak.
As we can see, C allows programmers to do bad things to memory. In java, all these problems are solved by
the advent of garbage collection. The operator new in java is roughly equivalent to malloc in C. When one
creates a reference (i.e. a pointer) to some memory via new, java sets up a separate internal reference to that
same memory. The java runtime system periodically checks all of its references, and if it notices that the
program no longer maintains a reference to some allocated memory, it de-allocates that memory. Thus to free
memory in java you do precisely what you should not do in C, just point the reference variable somewhere
else. Also it is not possible in Java to alter the contents of a free block, as can be done in C, since memory is
not freed until the program no longer contains references to it.
There is one more C memory allocation function of interest called calloc (contiguous allocation). We use
this function to allocate arrays on the heap. The instructions
4
int* A;
A = calloc(n, sizeof(int));
allocate a block of heap memory sufficient to store an int array of length n. Equivalently one can do
int* A;
A = malloc(n*sizeof(int));
The only difference in the two examples above is that calloc() sets its allocated memory to zero, while
malloc() does not. Note also that in both examples n can be a variable. Recall that one cannot allocate
variable length arrays on the stack. For instance int A[n] is not a valid declaration in C. As with any array,
the array name is a pointer to its zeroth element, so that the expressions A==&A[0] and *A==A[0] always
evaluate to true (i.e. non-zero). In the above examples, A is itself a stack variable, while the memory it points
to is on the heap. Pointers have a special kind of arithmetic. The expression A+1 is interpreted to be, not the
next byte after A, but the next int after A[0], namely A[1]. Thus *(A+1)==A[1], *(A+2)==A[2], etc. all
evaluate to true. This gives an alternative method for traversing an array, which is illustrated in the next
example.
#include
#include
#include
int main(int argc, char* argv[]){
int i, n;
int* A;
/* check number of arguments on the command line */
if( argc<2 ){
printf("Usage: %s positive_integer\n", argv[0]);
exit(EXIT_FAILURE);
}
/* check that the command line argument is an integer */
/* and if so, assign it to n */
if( sscanf(argv[1], "%d", &n)<1 || n<1 ){
printf("Usage: %s positive_integer\n", argv[0]);
exit(EXIT_FAILURE);
}
/* allocate an array of n ints on the heap */
A = calloc(n, sizeof(int));
/* initialize the array using the standard subscript notation */
for(i=0; i