7) C memory#
Today:
Pass by value Vs Pass by reference
C pools of memory
malloc
andcalloc
1. Pass by value Vs Pass by reference#
Functions can be invoked in two ways: Call by Value or Call by Reference. These two ways are generally differentiated by the type of values passed to them as parameters.
The parameters passed to the function are called actual parameters whereas the parameters received by the function are called formal parameters.
1.1 Call by Value in C#
In the call by value method of parameter passing, the values of actual parameters are copied to the function’s formal parameters.
There are two copies of parameters stored in different memory locations.
One is the original copy and the other is the function copy.
Any changes made inside functions are not reflected in the actual parameters of the caller.
Example of Call by Value in C#
The following example demonstrates the call-by-value method of parameter passing
1// C program to illustrate call by value
2#include <stdio.h>
3
4// Function Prototype
5void swapx(int x, int y);
6
7// Main function
8int main()
9{
10 int a = 10, b = 20;
11
12 // Pass by Values
13 swapx(a, b); // Actual Parameters
14
15 printf("In the caller:\n a = %d b = %d\n", a, b);
16
17 return 0;
18}
19
20// Swap functions that swaps two values
21void swapx(int x, int y) // Formal Parameters
22{
23 int t;
24
25 t = x;
26 x = y;
27 y = t;
28
29 printf("Inside the (callee) function:\n x = %d y = %d\n", x, y);
30}
Output:
Inside the (callee) function:
x = 20 y = 10
In the caller:
a = 10 b = 20
Thus the actual values of a
and b
remain unchanged even after swapping the values of x
and y
in the called function.
1.2 Call by Reference in C#
In the call by reference method of parameter passing, the address of the actual parameters is passed to the function as the formal parameters. In C, we use pointers to achieve call-by-reference.
Both the actual and formal parameters refer to the same locations.
Any changes made inside the function are reflected in the actual parameters of the caller function, too.
Example of Call by Reference in C#
The following C program is an example of a call-by-reference method.
1// C program to illustrate Call by Reference
2#include <stdio.h>
3
4// Function declaration (or prototype/signature)
5void swapx(int*, int*);
6
7// Main function
8int main()
9{
10 int a = 10, b = 20;
11
12 // Pass by reference
13 swapx(&a, &b); // Actual Parameters. Note that we pass directly the addresses of these two variables
14
15 printf("In the caller:\n a = %d b = %d\n", a, b);
16
17 return 0;
18}
19
20// Function to swap two variables by reference
21void swapx(int* x, int* y) // Formal Parameters
22{
23 int t;
24
25 t = *x;
26 *x = *y;
27 *y = t;
28
29 printf("Inside the (callee) function:\n x = %d y = %d\n", *x, *y);
30}
Output:
Inside the (callee) function:
x = 20 y = 10
In the caller:
a = 20 b = 10
Thus, the values of a
and b
get changed also in the caller function after swapping the values of x
and y
inside the callee.
Difference between the Call by Value and Call by Reference in C#
The following table lists the differences between the call-by-value and call-by-reference methods of parameter passing.
Call/Pass By Value |
Call/Pass By Reference |
---|---|
While calling a function, we pass directly the values of variables to it. Such functions are known as “Call By Value” |
While calling a function, instead of passing the actual values of the variables, we pass the address of the variables (location of variables) to the function. This is known as “Call By Reference” |
In this method, the value of each variable in the calling function is copied into corresponding dummy variables in the called function |
In this method, the address of the actual variables in the calling function is copied into the dummy variables in the called function. |
With this method, changes made to the dummy variables in the called function have no effect on the values of the actual variables in the calling function |
With this method, using addresses (and pointers associated with them), we have access to the actual variables and hence we can manipulate them |
In call-by-value, we cannot alter the values of the original variables through function calls |
In call by reference, we can alter the values of variables through function calls |
Values of variables are directly passed to functions |
Pointer variables are necessary in the function declaration/definition to access the addresses of the passed variables |
This method is preferred when we have to pass some small values that should not change |
This method is preferred when we have to pass a large amount of data to the function or when data need to be manipulated |
In conclusion, “Call/Pass by Value” means passing values as copies to the function, so that the original data is preserved and any changes made inside the function are not reflected in the original data, whereas “Call/Pass by Reference” means passing references to the memory locations of variables (in C, we use pointers to achieve the call by reference behavior), hence changes made inside the called function are reflected in the original caller values.
2. C pools of memory#
C has three different pools of memory.
static: global variable storage, permanent for the entire run of the program.
stack: local variable storage (automatic, continuous memory).
heap: dynamic storage (large pool of memory, not allocated in contiguous order. Can be fragmented).
Static memory#
Static memory persists throughout the entire life of the program, and is usually used to store things like global variables, or variables created with the static clause. For example, we saw the simple declaration of an
int
variable:
int int_var;
On many systems this variable uses 4 bytes of memory. This memory can come from one of two places. If a variable is declared outside of a function, it is considered global, meaning it is accessible anywhere in the program. Global variables are static, and there is only one copy for the entire program. Inside a function the variable is allocated on the stack.
It is also possible to force a variable to be static using the
static
clause. For example, the same variable created inside a function using thestatic
clause would allow it to be stored in static memory.
static int int_var;
Stack memory#
The stack is used to store variables used on the inside of a function (including the main()
function). It’s a LIFO, “Last-In,-First-Out”, structure. Every time a function declares a new variable it is “pushed” onto the stack. Then when a function finishes running, all the variables associated with that function on the stack are automatically deleted, and the memory they use is freed up. This leads to the local scope of function variables. The stack is a special region of memory, and automatically managed by the CPU – so you don’t have to allocate or deallocate memory. Stack memory is divided into successive frames where each time a function is called, it allocates itself a fresh stack frame.
Note that there is generally a limit on the size of the stack – which can vary with the operating system (it can be, say, 8MB). If a program tries to put too much information on the stack, stack overflow will occur. Stack overflow happens when all the memory in the stack has been allocated, and further allocations begin overflowing into other sections of memory.
A summary of the stack:
The stack is managed by the CPU, there is no ability to modify it
Variables are allocated and freed automatically
The stack it not limitless – most have an upper bound
The stack grows and shrinks as variables are created and destroyed, within the limit imposed by the OS
Stack variables only exist whilst the function that created them exists
Heap memory#
The heap is a large pool of memory that can be used dynamically, i.e, especially when you don’t know at compile time how big a variable is (for instance, think of an array that has user-defined length). This is memory that is not automatically managed – you have to explicitly allocate (using functions such as malloc
), and deallocate (e.g. free
) the memory. Failure to free the memory when you are finished with it will result in what is known as a memory leak – memory that is still “being used”, and not available to other processes. Unlike the stack, there are generally no restrictions on the size of the heap (or the variables it creates), other than the physical size of memory in the machine. Variables created on the heap are accessible anywhere in the program.
Heap memory requires you to use pointers.
A summary of the heap:
The heap is managed by the programmer, the ability to modify it is somewhat boundless
In C, variables are allocated and freed using functions like
malloc()
andfree()
The heap is large, and is usually limited by the physical memory available
The heap requires pointers to access it
An example of memory use:
1#include <stdio.h>
2#include <stdlib.h>
3
4int x; // global variable, stored in the static pool
5
6int main(void)
7{
8 int y; // dynamic stack storage
9 char *str; // pointer to type char
10
11 y = 4;
12 printf("stack memory: %d\n", y);
13
14 str = malloc(100*sizeof(char)); // create heap storage (a contiguous block) and assign its address to str
15 str[0] = 'm'; // str can be seen as an array, and we assign the character 'm' to its first entry
16 printf("heap memory: %c\n", str[0]);
17 free(str);
18 return 0;
19}
Output:
stack memory: 4
heap memory: m
The variable x
is static storage, because of its global nature. Both y
and str
are dynamic stack storage which is deallocated when the program ends. The function malloc()
is used to allocate 100 pieces of of dynamic heap storage, each the size of char, to str
. Conversely, the function free()
, deallocates the memory associated with str
.
Reading exercise#
For the advantages and disadvantages of using one type of memory rather than another, please see this resource.
3. malloc
and calloc
#
In the previous example, we have seen an example of usage of the function malloc()
.
malloc()
allocates a memory block of given size (in bytes).malloc()
returns a pointer to the beginning of the allocated block.
Note
malloc()
doesn’t initialize the allocated memory. If you try to read from the allocated memory without first initializing it, then you will invoke undefined behavior, which usually means the values you read will be garbage values.
calloc()
allocates the memory and also initializes every byte in the allocated memory to 0.If you try to read the value of the allocated memory without initializing it, you’ll get 0 as it has already been initialized to 0 by
calloc()
.
Unlike malloc()
, calloc()
takes two arguments:
Number of blocks to be allocated.
Size of each block in bytes.
Return Value:
After successful allocation in malloc()
and calloc()
, a pointer to the block of memory is returned otherwise NULL
is returned which indicates failure.
Example of a program that shows both malloc
and calloc
#
1// C code that demonstrates the difference
2// between calloc and malloc
3#include <stdio.h>
4#include <stdlib.h>
5
6int main()
7{
8 // Both of these allocate the same number of bytes,
9 // which is the amount of bytes that is required to
10 // store 5 int values.
11
12 // The memory allocated by calloc will be
13 // zero-initialized, but the memory allocated with
14 // malloc will be uninitialized so reading it would be
15 // undefined behavior.
16 int *allocated_with_malloc = malloc(5 * sizeof(int));
17 int *allocated_with_calloc = calloc(5, sizeof(int));
18
19 // As you can see, all of the values are initialized to
20 // zero.
21 printf("Values of allocated_with_calloc: ");
22 for (size_t i = 0; i < 5; ++i) {
23 printf("%d ", allocated_with_calloc[i]);
24 }
25 putchar('\n');
26
27 // This malloc requests 1 terabyte of dynamic memory,
28 // which is unavailable in this case, and so the
29 // allocation fails and returns NULL.
30 int *failed_malloc = malloc(1000000000000);
31 if (failed_malloc == NULL) {
32 printf("The allocation failed, the value of "
33 "failed_malloc is: %p \n",
34 (void*)failed_malloc);
35 }
36
37 // Remember to always free dynamically allocated memory.
38 free(allocated_with_malloc);
39 free(allocated_with_calloc);
40}
Output:
Values of allocated_with_calloc: 0 0 0 0 0
The allocation failed, the value of failed_malloc is: (nil)