03B+04A Dynamic Memory Allocation Memory Leaks
03B+04A Dynamic Memory Allocation Memory Leaks
2
Expected Learning Outcomes
Dynamic memory allocation & safe memory management
3
Motivation for dynamic allocation
4
Motivation for dynamic allocation
The size of global and local* variables must be
known at compile-time
int global_int; // sizeof(int)
int global_array[1000]; // 1000*sizeof(int)
void foo ()
{
int x = 5; // sizeof(int)
int arr[] = {1, 2, 3}; // 3*sizeof(int)
int *p = &x; // size of a memory address
}
e.g. Slither:
6
Case 1: board size is known at compile time
return EXIT_SUCCESS;
}
7
Case 2: user specifies board size
at run-time
#include <errno.h> // global error tracking – details later…
int get_board_size_from_user ()
{
printf("Please enter board size:\n")
long int size = -1;
bool ok = read_n(&size);
if(!ok)
{
errno = EIO; // global error tracking – details later…
}
return size;
}
8
Case 2: user-specified board size
…
int main()
{
size_t size = get_board_size_from_user ();
int board[size][size]; // is that ok?
// a variable size for local array VLA!
// not allowed in this course
9
A valid solution: slack memory
Allocate extra memory at compile-time, But what if
and use a smaller subset as needed size>MAX_SIZE?
#define MAX_SIZE 500
…
{
int board[MAX_SIZE][MAX_SIZE] = {0};
size_t size = get_board_size();
// fill only relevant cells:
for(int i = 0; i < size; i++)
{
for(int j = 0; j < size; j++)
{
board[i][j] = (i+1)*(j+1);
…
10
Compile-time size limit is problematic
11
Dynamic heap
1. Memory is allocated
0xffffff
and freed at run
time by the memory Stack
manager
Free space
2. The programmer
controls how Heap
much is allocated
and when Data (Global/Static)
3. Adapt based on
run-time conditions, Code (text)
available computer 0x000000
memory
12
Dynamic allocation
of heap memory
13
malloc(): allocating heap memory
14
malloc(): alternative syntaxes
15
calloc(): Alternative to malloc()
17
Efficiency: static vs. dynamic arrays
18
Rule: Always check if ptr is valid!
19
Example: a dynamic array of N complex numbers
typedef struct Complex {
double imag, real;
} Complex;
20
De-allocating
memory
21
free(): De-allocating memory
void free (void *p ); // <stdlib.h>
No error checking!
22
Good practice: set ptr to NULL after free()
int *p = malloc (sizeof(*p));
…
free (p);
p = NULL; // good practice
…
23
How much memory to free?
24
How much memory is de-allocated?
The memory manager (provided by the compiler
directly or via the operating system) keeps track of
how much memory was allocated, (e.g., by putting
some information right before the allocated address)
and thus knows how much memory to free
25
Q: Will the code free
elements arr[5]..arr[9]?
size_t n = 10;
int *arr = malloc
(n * sizeof(int));
…
free(arr + 5);
A. Yes! Do not forget: free()
B. Run-time bug must get the exact
same address
C. Compile error previously returned by
D. It depends… malloc(), calloc(),
or realloc().
26
Reallocating
memory
27
realloc(): change size for an existing array
29
Memory bugs:
Segmentation fault
32
Segmentation fault
33
Q: Seg-fault
Bob gets a "segmentation fault" while
working on his C course assignment.
What is a likely explanation?
34
Memory bugs:
memory leakage
35
Example: strdup(), a non-standard
library function for duplicating strings
A possible implementation:
char *strdup (const char *str) // <string.h>
{
char *dup = malloc (strlen (str) + 1); // +1 for '\0'
if (!dup)
{
return NULL;
}
strcpy (dup, str); // Copy the characters
return dup; // Return the new string
}
36
Is there a bug in this code?
Stack
>call<
void foo (char const* p) p
{ q
…
Heap
37
Is there a bug in this code?
Stack
>call<
void foo (char const* p) p
{ q
…
’a‘
’b‘
Heap
’0\‘
38
Is there a bug in this code?
>call<
Stack
void foo (char const* p) p
{ q
…
Memory leak: The allocated memory
is locked and we threw away the keys ’a‘
(pointer q) ’b‘
Heap
’0\‘
39
Don’t forget to free!
>call<
Stack
void foo (char const* p) p
{ q
…
}
Heap
away the keys… ’0\‘
40
Resource ownership schemes for
avoiding memory bugs
41
Resource ownership:
Who is responsible for cleanup?
• System resources must be managed carefully:
• Dynamically allocated memory
• Storage files
• Network connection
• I/O devices
• Speed/space
Efficiently allocating and freeing both space-wise (fragmentation) and time-wise (slow
42
Scheme #1: Fixed Ownership
a.k.a. clean thy own mess
that resource
43
Example for fixed ownership:
local stack variables
• Local stack variables are freed automatically when the
scope that declared them ends
• Pointer
• File handle
• Etc.
45
Example for dynamic ownership:
Returning a newly heap-allocated array
• We can choose to pass a memory address for a newly-allocated heap
memory
• This is a programmatic API decision - the compiler doesn’t know (or
care!) whether ownership of arr is fixed or dynmic
46
Can we have fixed ownership over heap-
allocated memory? Our decision!
• As programmers, we are allowed to decide whether we
want to have fixed ownership scheme for some heap
memory
47
Fix leaks by formally
Stack
considering API of ownership >call<
p
void foo (char const* p) q
{
char *q = strdup (p);
// do something with q
…
} // is this a leak?
Heap
48
Fix leaks by formally
considering API of ownership >call<
Stack
p
void foo (char const* p) q
{
char *q = strdup (p);
// do something with q
…
} // is this a leak?
Heap
q to the calling code entity
’0\‘
49
I. Solution 1 – free (fixed ownership)
>call<
Stack
void foo (char const* p) p
{ q
…
}
’a‘
’b‘
Heap
’0\‘
50
I. Solution 2 – transfer (dynamic ownership)
>call<
Stack
char* foo (char const* p) p
{ q
…
}
’a‘
’b‘
Heap
’0\‘
51
Free() or transfer rule
• Any code entity (eg a function) that owns a resource (eg a
pointer, a file) should either free it (fixed ownership) or
return it (dynamic ownership).
• If ownership is transferred, it must be documented (API
decision)
52
Scheme #3: Weak ownership
= access-only ownership
• A code entity may need to access (read or write)
the contents of a resource without truly owning it
(= no responsibility for memory de-allocation)
• This is also an API decision – the compiler is not
aware of fixed/dynamic/weak ownership
53
Example for weak (access-only)
ownership
• strcmp(const char *s1, const char *s2) has read access to
the memory of s1 and s2, but it does not get ownership over it:
• the compiler is responsibe for clearing the stack memory of s1
• the calling code entity must free s2
{
char s1[] = “hello”;
char* s2 = strdup(s2);
// strcmp gets weak ownership of s1 and s2
if (s2 && strcmp(s1, s2)) …
free (s2); // code entity frees s2 (user)
} // s1 is freed automatically (compiler)
54
Summary: Memory ownership & bugs
• Memory ownership schemes
• Fixed ownership – you own it, you free it
• Dynamic ownership - you own it, pass it on
• Weak ownership – access only
55