Welcome to CS3410 C Lab 2! This lab will give your more familiarity with pointers and memory in C and introduce you to dealing with structs. After completing each section, have a TA check over your work before you proceed.
To begin, download the clab2.c
file file to your VM or CSUG account. It contains some C functions as well as function stubs you will need to implement.
After completing each function, compile your program with gcc -std=c99 -Wall -Werror clab2.c -o clab2
. When you're done, you can run it with ./clab2
.
typedef struct {
int *buffer;
int buffersize;
int length;
} arraylist;
You can use a . to access the fields of a struct (again, like classes). If you have a pointer to a struct, you can use -> to both dereference and access.
arraylist a;
a.length = 5;
arraylist *a_ptr = &a;
a_ptr->length = 10;
(*a_ptr).length = 15;
That is, a->b
is the same as (*a).b
, but much easier to read and write.
In C, primitive values and even arrays are often declared on the stack. This is known as static memory or static allocation. This is fast and efficient, but a major drawback is that the size of objects on the stack must be known at compile-time. Therefore, such common tasks as dealing with variable-length arrays cannot be done using only static memory.
C also allows the programmer to use dynamic memory, which is asking the operating system for more memory at runtime, and then using that newly allocated memory. While allocating memory does take time, dynamic memory is incredibly flexible and powerful, since any amount of memory can be requested and used at runtime (only limited by the amount of memory on the system).
The two functions in C to deal with dynamic memory (defined in stdlib.h
) are malloc
and free
. malloc
allocates new dynamic memory. It takes a single argument, the amount of bytes to allocate, and returns a void*
pointer to the newly allocated memory. A void*
pointer can point to objects of any type (e.g. int
, float
, or even a struct potato_salad
) and can be cast to the appropriate type the programmer wants to use. Commonly used with malloc
is the sizeof
operator, which returns the size in bytes of any type defined in the program.
int *int_ptr = (int*)malloc(sizeof(int)); //allocates room for one int
int *int_ptr = (int*)malloc(5 * sizeof(int)); //allocates room for five ints
Any memory allocated using malloc
must eventually be freed by a corresponding call to free
. The free
function takes a single argument: a pointer that was allocated using malloc
. It releases the associated memory back to the operating system. After a pointer has been freed, accessing or using that pointer again leads to undefined and unstable behavior. Don't do this!
The code below is a simple example of dynamic memory in C, including the use of pointers as arrays.
int *int_ptr;
int_ptr = (int*)malloc(sizeof(int));
*int_ptr = 5;
printf("I stored the int %d at address %p\n", *int_ptr, int_ptr);
free(int_ptr); // don't forget to free memory when you're done with it!
/* Since arrays in C are just contiguous regions of bytes, pointers can also
* be used to point to arrays. */
int *int_arr;
int i;
// here, we malloc enough space for 5 ints - creating an int array of length 5!
int_arr = (int*)malloc(5 * sizeof(int));
for (i = 0; i < 5; i++) {
int_arr[i] = i;
}
// now, int_arr points to the int array [0;1;2;3;4]
free(int_arr);
rand_array
function
Using your new knowledge of pointers (and arrays as pointers), implement the rand_array
function to allocate, initialize, and return a new array of random integers of a specified length. Use the rand_num
function we provide to populate each array element with a random int.
Note that this function is slightly different than last lab.
initialize_complex_array
function
Now implement the initialize_complex_array
function to allocate, initialize, and return a new array of complex numbers made from the values in the input arrays. Note that complex_number
is defined at the top as a struct.
calculate_magnitude
function
Take in the pointer to a complex_number
struct, calculate the magnitude of the complex number, and return it as a float. To do so, you'll want to use the sqrt function from the math
library; to do so, import math.h
and add the flag -lm
when you compile your code. The -lm flag links in the math library, meaning that the compiler will bring in code from the math library as appropriate; you'll discuss this much more rigorously later in the semester.
calculate_magnitudes
function
Take in the pointer to an array of complex_number
structs, calculate the magnitude of each complex number, and return the values as a float array.
Note the steps that lead you through the parts of using malloc. This is the same as what you've done before, but we want you to consider exactly what's happening.
If you're on your own machine, install the valgrind tool with sudo yum -y install valgrind
; it's already installed if you're on CSUG. Then run it on your lab 1 program (valgrind ./clab2
). This is a tool used by C programmers to help find memory issues in their programs. Valgrind should report errors indicating that there was heap (dynamic) memory still in use when the program terminated. This is because something very important is missing from the main
function we provided. Find it, and fix it!