Stack and Heap Memory Stack resident variables include: Parameters passed to functions Variables declared inside basic blocks that are not declared static. For stack resident variables, the size of the variables must be known at compile time, and cannot be changed during run time.
Stack and Heap Memory Heap resident variables include: Variables declared outside of all functions. Variables declared inside basic building blocks that are declared static. Memory areas dynamically allocated at run time with malloc(), calloc(), or realloc().
Pointers and Dynamic Allocation of Memory The system maintains a second storage area called the heap Permits Postponing the decision on the size of the memory block needed to store an array Blocks of storage are allocated in response to specific requests while the program is running Using a section of memory for the storage of an array at one point in time then Freeing that memory for other uses when that memory is no longer needed
Memory allocation functions can be used to dynamically allocate an area of memory to be used at run time. Heap memory is used for these variables! place the compiler directive, #include <stdlib.h> near the top of the file.
Pointers and Dynamic Allocation of Memory When memory is allocated, the allocating function (such as malloc(), calloc(), etc.) returns a pointer of type void (depending on the compiler) void * indicates a pointer to untyped memory In C++, will have to cast the returned value to the specific type needed; not necessarily the case in C use free to release allocated space when no longer needed, so that it can be reused
Static and Dynamic Allocation of Memory // local declaration int x; // local declarations int array[5]; array x Stack Stack (a) Stack Memory Allocation // local declarations int *array; array = (int *)malloc(…); array // local declaration int x; x Stack Heap Stack (b) Dynamic Memory Allocation Heap
malloc() void *malloc(size_t size); can be used to dynamically allocate an area of memory to be used at run time. Heap memory is used for these variables! Contiguous allocation No initialization of memory Must #include <stdlib.h> Takes one argument: total amount of memory required
malloc() malloc() advantages: Permits positioning the decision on the size of the memory block needed to store a large data type, such as a large (maybe sequence of) structure or an array. Permits using a section of memory for the storage of a large data type at one point in time, and then when that memory is no longer needed, it can be freed up for other uses.
malloc() advantages (cont'd) Is better when storing large data types, such as a large structure or array, because copying large data structures on the stack is very inefficient. Stack memory is a small finite space, therefore it is better to store large data structures in a heap to avoid stack overflows.
malloc() Examples int *data = malloc(50 * sizeof(int)); char *field = malloc(31 * sizeof(char)); const int n = 100; char double = malloc(n * sizeof(double));
malloc() Prior to C99, it was necessary to cast the pointer returned by the memory allocation functions. Although it is not necessary in C, it is still a good idea to cast. Examples int *data = (int *)malloc(50 * sizeof(int)); char *field = (char *)malloc(31 * sizeof(char)); const int n = 100; char double = (double *)malloc(n * sizeof(double));
calloc() void *calloc(size_t num_elements, size_t element_size); Used to dynamically create an array in the heap Contiguous allocation Memory initialized to base value of element Must #include <stdlib.h> Takes two arguments Number of array elements Amount of memory required for one element
calloc() Examples int stringSize = 25; int *data; data = (char *) calloc(stringSize, sizeof(char)); int arraySize = 50; int *scores; scores = (int *) calloc(arraySize, sizeof(int));
free void free(void *ptr); Used to dynamically release memory back to heap Contiguous deallocation Must #include <stdlib.h> Takes one argument: pointer to beginning of allocated memory
free() Example int arraySize = 50; int *data = (int *) malloc(arraySize * sizeof(int)); . free(data);
Address Arithmetic We have seen that we can add and subtract values from a pointer. The meaning of doing an arithmetic operation on a pointer is actually a little more involved (and natural) than simply adding the value to the pointer. Arithmetic operations on pointers are always done in units related to the size (in bytes) of the object the pointer points to.
Address Arithmetic For example, consider the following code: char s[] = “The cat sat on the mat”; int x[] = {1, 2, 3, 4, 5}; double f[] = {3.2, 5.43, 6.99, 4.24, 6.11}; char *sptr = s; int *xptr = x; double *fptr = f; Each element of “s” is 1 byte long, each element of “x” is 4 bytes long, and each element of “f” is 8 bytes long (a double precision floating point number occupies 64 bits).
Address Arithmetic Let’s assume that the first element of s[] is at location 20,000 in memory, the first element of x[] is at location 22,000 in memory and the first element of f[] is at location 24,000. With the above initialization statement, then initially sptr=20,000, xptr=22,000 and fptr=24,000. Now consider the result of each of the following increment statements: sptr++; xptr++; fptr++; In each case, the BYTE length of the data type of what the pointer is pointing to is added. That is, after these increments sptr=20,001, xptr=22,004, and fptr=24,008.
Address Arithmetic This is fortunate, since this means that the increment positions the pointer to the start of the next element of the array – which is generally just what we want. This holds for all arithmetic operations on pointers. For example the statement: xptr+=3; would actually add “3*4”=”12” to the value of xptr, and will move xptr 3 elements forward in the array.
Using a pointer to process an array of numbers This program demonstrates (1) how to process command line parameters and (2) how to process an array of ints using a pointer. /* p20.c */ /* This program illustrates the use of pointers in */ /* reading and processing an array of integers */ /* It also shows how to access command line args */ /* An upper bound on the number of ints to be read */ /* must be specified on the command line.. */ /* p2 1000 */
Using a pointer to process an array of numbers #include <stdio.h> int main( int argc, /* number of command line args */ char* argv[]) /* array of pointers to args */ { int* base; // points to start of the array int* loc; // array index int max; // maximum number of values to read in int count; // actual number of values read in int largest; // largest number in the array int i; if (argc < 2) /* Make sure at least one arg was given */ printf("Usage is p20 upper-bound \n"); exit(1); }
Processing the command line argument The argc parameter contains the number of command line parameters, including the program name itself. Thus a command such as ./a.out 300 will cause argc to be set to 2. It's very important to ensure that the user has provided the number of parameters you need before you attempt to process them! if (argc < 2) /* Make sure at least one arg was given */ { printf("Usage is p20 upper-bound \n"); exit(1); }
Processing the command line argument The argc parameter contains the number of command line parameters, including the program name itself. Thus a command such as ./a.out 300 will cause argc to be set to 2. It's very important to ensure that the user has provided the number of parameters you need before you attempt to process them! if (argc < 2) /* Make sure at least one arg was given */ { printf("Usage is p20 upper-bound \n"); exit(1); }
Processing the command line argument This program expects that a numeric value representing the maximum number of values that are present in the standard input will be present on the command line. /* The pointer argv[0] points to the name of the program (p20) */ /* argv[1] points to the first command line argument */ /* The atoi() function converts the ascii character */ /* representation an integer to a binary int value. */ max = 0; max = atoi(argv[1]); if (max <= 0) { printf("upper-bound must be a positive integer \n"); exit(2); }
Allocating storage for the array of ints Note that the size of the area allocated must be specified in bytes. count = 0; base = (int *)malloc(sizeof(int) * max); loc = base; // assign the starting address to loc
Processing each element of the array Note that the size of the area allocated must be specified in bytes. // read data while(!feof(stdin) && count < max) { scanf("%d", loc); printf("%d\n", *loc); count++; loc++; }
Processing each element of the array Note that the size of the area allocated must be specified in bytes. // Find largest loc = base; largest = *loc; for(i = 0; i < count; i++) { if(largest < *(loc + i)) largest = *(loc + i); } printf("largest = %d\n", largest); return 0;