Pointers and Memory Allocation
Goals
This laboratory exercise provides practice with basic elements of pointers, addresses, values, and memory allocation in C.
Printing Memory Addresses
-
Write a short C program that declares and initializes (to any value
you like) a
double, anint, and a string. Your program should then print the address of, and value stored in, each of the variables.-
Use the format string
"%lu"to print the addresses as long unsigned integers.To avoid compiler warnings, you will need to expicitly cast the address values used as arguments to
printfasunsigned longReminders:
-
You can use the
&character to find addresses - 1 byte = 8 bits, and a 32-bit integer requires the space of 4 bytes.
-
You can use the
- Draw a small memory diagram showing the location of each of the variables in your program. Are they allocated in the same order that you declared them? Is there any empty space between them?
- Modify your program by rearranging the variable declarations and/or changing the length of the string. (In particular, try a string that uses 5 or 7 bytes, including the null terminator.) Does this change the results you got previously?
-
The take-home message:
Small changes within a program can change how memory is laid out for a given program. The compiler will try to arrange memory for optimal performance, and this may include aligning variables with 4-byte boundaries. For C programmers, this can sometimes mean that a program which appears to work correctly (but in fact overwrites the end of an array), can suddenly stop working due to seemingly innocuous changes—for example, changing the order in which variables are declared.
Allocating and Freeing Memory
The "Variable-Length Array (VLA)" option within 1999 Standard C allows yet another mechanism for declaring arrays, as follows.
void takePictures(int numPics)
{
Picture frames[numPics];
...
}
The size of an array is a variable (numPics), and a value is
assigned to this variable before space for the array is allocated. This
technique allows the user to specify the size of an array at run time; but once
the array is declared, its size cannot be changed.
-
What does the compiler know about the size of VLAs, versus
statically declared arrays, versus dynamically allocated memory? To
answer this question, consider the following program.
/* Program to compare sizes of static arrays, VLAs, and malloced memory. */ #include #include int main() { int length = 5; int staticArray[5]; /* Compiler statically allocated array */ int varLenArray[length]; /* Program dynamically allocates array */ /* Programmer dynamically allocates memory */ int * dynamicArray = malloc(length * sizeof(int)); if (dynamicArray==NULL) { /* Verify memory was available and allocated */ printf("unable to allocate dynamic array. exiting."); return 1; } printf("sizeof(int) = %lu\n", sizeof(int) ); printf("sizeof(staticArray) = %lu\n", sizeof(staticArray) ); printf("sizeof(varLenArray) = %lu\n", sizeof(varLenArray) ); printf("sizeof(dynamicArray) = %lu\n", sizeof(dynamicArray) ); free(dynamicArray); /* Free memory allocation */ } - What output do you expect this program to produce?
- Copy, compile, and run the program to verify your predictions.
-
You might notice that the dynamic allocation of a VLA versus a
call to
mallocis subtly different. What test can we make withmallocthat we cannot with a VLA? What does this suggest about the runtime verifiability of VLAs?
When arrays were first discussed, an early application was to use the
Scribbler 2 to take 3 pictures and then display those pictures in the
order they were taken. Program
scribbler-movie.c
expands the former program slightly to take numPics pictures,
display them in order, and then display them in reverse order.
-
Copy and run
scribbler-movie.c, and then review how the program works. -
In this problem, we explore the alternative strategy using dynamic
memory allocation.
-
Within
scribbler-movie.c, replace the declaration
with the linesPicture pics[numPics];
Notes:Picture* pics; pics = malloc (numPics * sizeof (Picture));-
In this revised declaration,
picsis a pointer to an array of pictures. That is,picsidentifies the location of an array where each element has typePicture. -
In the first line above,
picsonly indicates a location for an array. Thus the program must allocate space for the array separately, in the second line. Themallocstatement asks the C library to perform this memory allocation. -
Once declared and initialized, references to
the
picsarray are exactly the same as in the original version.
-
In this revised declaration,
- Add the corresponding lines to verify the memory allocation (printing an error message and exiting if it fails).
- Add the corresponding line to free the memory when it is no longer needed.
-
Recompile and run the revised program
scribbler-movie.c. -
Draw a diagram of main memory for both the original and revised
versions of
scribbler-movie.c. In the diagram, show what variables are stored on the run-time stack and what information (if any) is stored elsewhere.
-
Within
-
Modify
scribbler-movie.cfurther to obtainscribbler-movie-10.c, so that every time the Scribbler 2 robot takes 10 pictures, it displays all of the pictures (from the first to the most recent). This version of the program should not display the pictures in reverse order.
Consider a problem where the Scribbler 2 is to take a long sequence of pictures. After each picture, the robot is to turn left slightly. When a multiple of 10 pictures has been taken, all pictures are to be displayed (from the first to the last).
This problem is similar to the
revised scribbler-movie-10.c program, except that the
user does not specify an initial bound on the number of pictures to
be taken. Because computers have a finite-sized memory, eventually
the space required for the pictures will be exhausted. However, this
process could go on for some time before memory-allocation issues
would be expected to arise. (In what follows, we largely will ignore
this issue.)
This type of problem is not uncommon on various Web sites which display pictures of recent activities. For example, weather forecasting sites may display the radar images for a region from the past half hour, hour, or longer.
One approach to this problem utilizes an array to store
pictures, just as in scribbler-movie-10.c.
-
The program starts with a
Picturearray of a specified size (e.g., space for 5 pictures). - Pictures are added to the array until it is full.
-
When all elements of current the array designate pictures:
- new memory is allocated for twice the space of the previous array
- pictures are copied from the old array to the new memory
- the old space is deallocated (freed)
-
the
picsvariable is updated to refer to the new array
-
Copy
scribbler-movie-10.cto programscribbler-movie-expand.c, and modify it so it implements this new approach for handling many pictures without an initial designation of how many pictures might be taken.
Memory Leaks and Other Problems
-
Consider the following program.
#include #include const int FALSE = 0; const int TRUE = 1; int main() { int done = FALSE; int j=0; while (!done) { int n = 100000000; int* a = (int*)malloc(n * sizeof(int)); int i; for (i=0; i < n; i++) a[i] = i; j++; printf("%d\n", j); } return 0; } - What is wrong with the program? What do you expect it to do when run?
- Now copy the program and run it. On my machine, it prints numbers up to around 30 before it crashes. How about yours? Do you understand why it crashes?
-
Add the following code immediately after
the
malloccall to confirm your understanding.
The library functionif (a==NULL) { perror("Error allocating memory"); exit(EXIT_FAILURE); }perror(), declared instdio.h, prints a message regarding the most recent error that occurred in any system or C library call. Thus, with this placement,perrorwill print any error that may occur related tomalloc. (We will discuss system calls later in the course.)If you still are not sure why the error occurred, please ask.
Detecting Leaks
In the next few exercises, you will experiment with a (non-GNU) Linux
tool named Valgrind that can detect and report on
several types of errors related to dynamic memory
management. Actually, Valgrind is a suite of debugging
tools; the specific Valgrind tool we will use is
called Memcheck. According to the documentation at
http://valgrind.org,
Valgrind
is pronounced with a short i (like grinned), and the origins of
the name are related to Norse mythology.
Valgrind is a "virtual machine", which means that you will run
Valgrind, and it will invoke your executable code line by
line. This allows it to monitor your use of memory and report related
errors. It also adds a lot of overhead, so you may notice that it runs
slowly.
- Modify your program from the previous exercise so that it allocates (and fails to free) only ten arrays or so.
-
Run your program with
Valgrind, using a command like the following. (For future reference, if your program takes command-line arguments, you can simply add these to the end of the command line.)valgrind --leak-check=yes ./myprog
Your output will include some header information about
Valgrind, then the output of your program, and then some diagnostic information about the memory leak.Do not be misled by the line that says
"ERROR SUMMARY: 0 errors from 0 contexts"
This apparently relates to specific error types. Continue reading, and you should see"malloc/free: 10 allocs, 0 frees"
and also the following.==22813== LEAK SUMMARY: ==22813== definitely lost: 0 bytes in 0 blocks. ==22813== possibly lost: 400,000,000 bytes in 10 blocks.
-
Modify your code from the previous exercise to free the memory you have allocated. Note that you will need a call to free in each loop iteration, so that you can free the memory before you lose the pointer to it!
Now rebuild your code, and run it with
Valgrindto see the improved output message. -
In this exercise, you will experiment with a few more memory-related
errors
Valgrindcan catch.-
Add an extra call to
free()somewhere in your program. Then rebuild your program and take a lookValgrind'soutput. (After you have done so, remove the offending call again.) -
Another common error that
Valgrindcan catch is accessing memory after it has been freed. To test this, you can add statements such as the following immediately after your call tofree(). Go ahead and try it, noting thatValgrindtells you the line numbers where the errors occur, and then remove the offending code.a[0] = 5; printf("a[0]=%d\n", a[0]); -
Valgrindcan also tell you when you access elements that are out-of-bounds of an allocated memory block. Modify your program to test this, noting what informationValgrindgives you about the error. (Then remove the error afterwards.)Unfortunately,
Valgrindcan not detect out-of-bounds errors with statically allocated arrays. It can only do this for dynamically-allocated memory.
-
Add an extra call to
-
Look at the on-line documentation for
Valgrind: http://valgrind.org. In particular, I suggest reading quickly through the "Quick Start" information, and also Sections 4.1 and 4.3 in the "User Manual".
