0% found this document useful (0 votes)
51 views56 pages

L21 DynamicAllocation

This document discusses dynamic memory allocation in C using malloc and free. It begins by explaining that malloc and free allow programs to allocate memory at runtime rather than having to declare fixed sizes. It then provides examples of using malloc to allocate structs and checking for errors, as well as freeing memory. The document cautions that memory should not be accessed after being freed and variables still point to freed memory. It also discusses realloc and memory leaks that can occur if memory is not freed.

Uploaded by

rosh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
51 views56 pages

L21 DynamicAllocation

This document discusses dynamic memory allocation in C using malloc and free. It begins by explaining that malloc and free allow programs to allocate memory at runtime rather than having to declare fixed sizes. It then provides examples of using malloc to allocate structs and checking for errors, as well as freeing memory. The document cautions that memory should not be accessed after being freed and variables still point to freed memory. It also discusses realloc and memory leaks that can occur if memory is not freed.

Uploaded by

rosh
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 56

Dynamic Allocation 

Dynamic Storage
 Example: We need space to allow the user to enter some data,
but how much?
 80 bytes? 132? 1? 256? 10000?

 Two Friends from the C library: #include <stdlib.h>


 malloc()
 free()

 These friends create and manage the heap for us, allowing us
to grab storage at run time
malloc()

struct r {
char name[40]; Stack rp
double gpa;
struct r *next;
};
struct r *rp;
rp = malloc(sizeof(struct r));
Heap
if (rp == NULL){ 56 bytes
/* Handle Error! */
} Data
 Options for handling error
 Abort


Ask again
Save user data
Code
 Ask for less
 Free up something
Don’t Do This!
rp = malloc(sizeof struct r);

// Code foolishly inserted here!

if(rp == NULL)

 Be afraid.

 Using rp before you check it is a crash waiting to happen.


Idiomatic But Safe
if((rp = malloc(sizeof struct r)) == NULL){
/* Handle Error Here */
}

 This is the way it’s done in real life.


 Don’t mix up the = and == operators!

 Alternate syntax (because NULL==0):

if( !(rp = malloc(sizeof struct r)) ){


/* Handle Error Here */
}
Escape From Type-Checking
 malloc() returns a pointer to at least as many bytes as we requested.

 But what is the type of this pointer and how do we use it to store items of a
different type?
 malloc() is declared as “void *malloc(unsigned long)”

 C uses the idiom “pointer to void” for a generic pointer

 To be safe, you should cast this pointer into the correct type so that type-
checking can work for you again!

int *ip;
ip = (int *)malloc(sizeof int);

 Otherwise, the compiler will silently cast your “void *” pointer into any other
kind of pointer without checking
Done With a Chunk of Storage
 When you’re done with a chunk of storage, you use free() to make it
available for reuse.
 Remember, C doesn’t do garbage collection

 From our previous example

free(rp);

returns the memory back to the heap for re-use by someone else
 You must not use the value in rp after the call to free(), nor may you
dereference the memory it points to!
 There’s no guarantee what’s at *rp after you call free() – assume it is
garbage data!
 With modern C libraries, free(NULL) does nothing but isn’t an error
After free()
 From our previous example

free(rp);

 The variable rp still exists.


 It is a pointer to struct r

 But what it points to is now garbage data.


 We should never dereference rp again: *rp

 We can, however, assign a new value to rp


 That is okay – make it point somewhere else

 The compiler will NOT help you with this.


 Mistakes will cause run-time errors
Other memory allocation functions
 void *malloc(size_t n);
 Allocates (at least) n bytes of memory on the heap, returns a pointer to it
 Assume memory contains garbage values

 void *calloc(size_t num, size_t size);


 Allocates (at least) num*size bytes of memory on the heap, returns a
pointer to it
 Memory will be zero’ed out.

 void *realloc(void *ptr, size_t n);


 Reallocates (at least) n bytes of memory on the heap, returns a pointer to it
 Copies the data starting at ptr that was previously allocated
 Often used to expand the memory size for an existing object on the heap
Pointer Video
 https://www.youtube.com/watch?v=5VnDaHBi8dM
Handling Persistent Data 
Persistent Data I
char *foo(void)
{
static char ca[10];
return ca;
}

 Anyone calling this function now has access to ca in this block. Could be
dangerous. Why?

 Note that this approach is not dynamic


Example
char *strFromUnsigned(unsigned u)

static char strDigits[] = “?????”;

char *pch;

pch = &strDigits[5];

do

*--pch = (u % 10) + '0';

while((u /= 10) > 0);

return pch;

}
Problem in use
strHighScore =
strFromUnsigned(HighScore);
.
.
.
strThisScore =
strFromUnsigned(ThisScore);
Persistent Data II
char *foo(void)

char ca[10];

return ca;

 Since ca was allocated on stack during function call pointer returned is now
pointing to who-knows-what

Bad
Persistent Data III
char *foo(void)
{
char *ca = malloc(...);
/* error checking but no free */
return ca;
}

 This actually works, but the caller needs to know that they’re
responsible for the free()
Memory Leaks
 Memory leaks occur when the programmer loses track of memory
allocated by malloc or other functions that call malloc

void foo(void)

char *ca = malloc(...);

/* no free */

return;

Bad
Memory Management
 Some functions that call malloc
 calloc
 strdup
 regcmp
 others...

 C doesn’t do automatic memory management for efficiency


reasons
 If you want to manage memory...do it yourself!
calloc()
void *calloc(size_t num, size_t size);
 Call malloc() to find space for num new allocations

 Initialize the space to zero (0)

 Not much to discuss…


realloc()
ptr = realloc(ptr, num_bytes);

 What it does (conceptually)


 Find space for new allocation
 Copy original data into new space
 Free old space
 Return pointer to new space
realloc()

ptr in-use Before

free After

ptr
realloc: What might happen

ptr unused Before

ptr After
realloc
 Realloc may return
 same pointer
 different pointer
 NULL

 Is this a good idea?

cp = realloc(cp, n);
1. Yes
2. No
3. Sometimes
Realloc
 Is this a good idea?

cp = realloc(cp, n);
 No!

 If realloc returns NULL cp is lost

 Memory Leak!
How to do it properly
void *tmp;

if((tmp = realloc(cp,...)) == NULL)

/* realloc error */

else

O!
N
{

cp = tmp;

free(tmp);

}
Additional Edge Cases
 realloc(NULL, n)  malloc(n);

 realloc(cp, 0)  free(cp); // only on some compilers

 These can be used to make realloc work in a single loop design


to build a dynamic structure such as a linked list.
Example
int size = 0; /* Size of "array" */
int *ip = NULL; /* Pointer to "array" */
int *temp;
int i;
char buffer[80];
while(fgets(buffer, 80, stdin) != NULL) {
size++;
if((temp = realloc(ip, size*sizeof(*temp))) == NULL){
fprintf(stderr, "Realloc failure\n");
exit(EXIT_FAILURE);
}
ip = temp;
ip[size-1] = strtol(buffer, NULL, 10);
}
Dynamic Allocation: What can go wrong?
 Allocate a block and lose it by losing the value of the
pointer
 Allocate a block of memory and use the contents without
initialization
 Read or write beyond the boundaries of the block

 Free a block but continue to use the contents

 Call realloc to expand a block of memory and then –


once moved – keep using the old address
 FAIL TO NOTICE ERROR RETURNS
Questions? 
Question
Is there anything wrong with the following code that frees all the nodes in a
linked list?

for (struct node *p = Head; p != NULL; p = p->next)


free(p); struct node *t;
for (struct node *p = Head; p != NULL; p = t) {
t = p->next
A. The head of the list doesn’t get freed free(p);
}

B. The code is satisfactory as it stands

C. The reinitialization uses memory that has been freed

D. The code uses free() multiple times on the same pointer


Malloc Implementation 
Outline
 K&R Malloc Implementation
 Headers
 Heap Layout
 Allocating the Correct Amount of Memory
 malloc(): Getting the memory for work
 free(): Recycling the memory when done
 morecore(): OS requests for real memory
Memory Management
 The K&R implementations of malloc() and free() are one of
many ways to implement these.
 We’ve refactored the code a bit. It was written for
compactness and efficiency in the 1970s C environment; things
have changed and we can afford to be a little bit more verbose
for clarity.
 It’s not necessarily the most efficient, and certainly not the
only way:
 http://codinghighway.com/2013/07/13/the-magic-of-wrappers/
 http://www.inf.udec.cl/~leo/Malloc_tutorial.pdf
 https://danluu.com/malloc-tutorial/
How K&R Malloc Works
 Linked list to track free memory
 Located in the Heap
 Circular Linked List

We ask the system for big chunks of memory and then split out
small blocks as requested

 This is the Free List


 Contains memory available to be malloc’ed
 Once memory is malloc’ed, we don’t track it until it is free’d
How K&R Malloc Works
 void *malloc(size_t n);
 Delete a node from the free list
 Return a pointer to that node to the program

 void free(void *ptr)


 Insert the node ptr into the free list

 Note: We’ve renamed them _malloc() and _free() in our code


to keep from conflicting with the malloc() and free() from the C
library
 There are a number of C library functions that use malloc() and
free(); we don’t want override the library versions until our
versions work properly!
Question
In the K&R implementation of malloc(), the data structure in the heap
is a:

A. Binary Tree

B. Hash Table

C. Circular Linked List

D. Array of Structs
Question
In the K&R implementation of the heap management, free() does the
following:

A. Deletes a node from the free list

B. Inserts a node in the free list

C. Zeroes out the bytes on the heap

D. Reduces the size of the heap


How K&R Malloc Works
 Each node on the free list is available memory
 Everything on the free list is in heap memory areas that malloc
has requested from the system
 Other functions can request heap space from the system, but
that space will never show up on malloc’s free list

 Consider this:
 We want each block in the free list to be as large as possible
 So it is more likely to have enough bytes to satisfy a malloc() call
 Therefore when we free a block adjacent to our other memory on
the heap, we should merge that block into the adjacent block.
A Node in the Free List

Points to next free block


Zero+ filler bytes for alignment

size

typedef Header Address returned to user


A block returned by malloc
How K&R Malloc Works
 Design choices
 Linked List is ordered by memory address (in the heap)
 We could also order the linked list from largest to smallest size
 There are many other ways we could implement malloc()

 Size in K&R malloc is number of units


 Not number of bytes
 A block is sizeof(Header) rounded up to the strictest alignment
boundary
 A block is 16 bytes in this implementation
Section of K&R Code
struct header { /* block header */
struct header *next; /* next block if on free list */
unsigned size; /* size of this block in Headersize units */
};
typedef struct header Header;

#define ALIGNBOUNDARY 16 /* align memory on 16 byte boundaries */


static int Headersize /* rounded up; use in place of sizeof(Header) */
= ((sizeof(Header) - 1) / ALIGNBOUNDARY + 1) * ALIGNBOUNDARY;

Points to next free block sizeof(Header Headersize


)
Zero+ filler bytes for alignment
8 16
size
12 16

typedef Header Address returned to user 16 16


20 32
A block returned by malloc
Returned by Malloc

Points to next free block


Zero+ filler bytes for alignment

size

typedef Header Address returned to user


A block returned by malloc

 If the variable
Header *p;
points to the beginning of a block, where does
(char *)p + Headersize
point?
Heap Layout

In In In
use use use

Free, owned by malloc


freep
in use In use, owned by malloc

Not owned by malloc

 K&R ch. 8

 One of those free blocks has user data size 0 and isn’t in the heap,
so it is never handed over by malloc().
Section of K&R Code
static Header base; // empty list to get started with
static Header *freep = NULL; // start of free list

/* malloc: general-purpose storage allocator */


void *malloc(unsigned nbytes)
{
Header *p, *prevp;
unsigned long nunits;

/* Allocate memory in Headersize units, so round up request */


nunits = (nbytes + Headersize - 1) / Headersize + 1;

• The key here is alignment with the Header’s size


• You need (just) enough bytes to cover the program’s request and the header
information
• Suppose that the Header consists of 16 bytes [Headersize = 16]
• Take a look at different requests for sizes as represented by nbytes
nbytes 7 15 16 17 32 33

nunits 2 2 2 3 3 4
Section of malloc() Code
if (freep == NULL) {
freep = &base;
base.size = 0;
base.next = &base;
if (morecore(nunits) == NULL)
return NULL; /* none left; give up */
}

0 nunits free
base
next size

freep *
Section of malloc() Code

for (prevp = freep, p = prevp->next; p->size < nunits;


prevp = p, p = p->next) {
if (p == freep)
if ((p = morecore(nunits)) == NULL)
return NULL; /* none left; give up */
}

freep prevp p

In In In
use use use
Section of malloc() Code
if (p->size == nunits)
prevp->next = p->next;
else {
p->size -= nunits;
p = (char *)p + p->size * Headersize;
p->size = nunits;
}
freep = prevp;
return (char *)p + Headersize;

freep (char *)p + Headersize

prevp p
p 3 7

10
5 7 5
3

Let nunits = 7, and follow how the free block is identified and returned
Pointer Sent to free()

Points to next free block


Zero+ filler bytes for alignment

size

typedef Header Address returned to user


A block returned by malloc

 If the variable
char *ap;
points to the address returned to the user, where does
(Header *)((char *)ap – Headersize)
point?
Section of free() Code
void _free(void *ap)
{
Header *bp, *p;

bp = (Header *)(ap - Headersize);

/* Find p such that the freed block is between p and p->next */


for (p = freep; !(p < bp && bp < p->next); p = p->next)
/* is *bp at either end of the arena? */
if (p >= p->next && (bp > p || bp < p->next))
break;

If p >= p->next, we’re


have to be here
bp
If bp < p->next, then bp is If bp>p then bp is to
to the right the arena p p->next the left of the arena
freep

In In In
use use use
Free(): Below and Above In Use
/* Look to see if we're adjacent to the block after the freed block */
if ((char *)bp + bp->size * Headersize == (char *)p->next) {
bp->size += p->next->size;
bp->next = p->next->next;
} else
bp->next = p->next;

/* Look to see if we're adjacent to the block before the freed block */
if ((char *)p + p->size * Headersize == (char *)bp) {
/* add the freed block to the block at p */
p->size += bp->size;
p->next = bp->next;
} else
p->next = bp;
freep = p;
p bp ap p->next

5 3 7 5
Free(): Below In Use
/* Look to see if we're adjacent to the block after the freed block */
if ((char *)bp + bp->size * Headersize == p->next) {
bp->size += p->next->size;
bp->next = p->next->next;
} else
bp->next = p->next;

/* Look to see if we're adjacent to the block before the freed block */
if ((char *)p + p->size * Headersize == bp) {
/* add the freed block to the block at p */
p->size += bp->size;
p->next = bp->next;
} else
p->next = bp;
freep = p;

p
bp ap p->next

10
5
3 7 5
Free(): Above In Use
/* Look to see if we're adjacent to the block after the freed block */
if ((char *)bp + bp->size * Headersize == p->next) {
bp->size += p->next->size;
bp->next = p->next->next;
} else
bp->next = p->next;

/* Look to see if we're adjacent to the block before the freed block */
if ((char *)p + p->size * Headersize == bp) {
/* add the freed block to the block at p */
p->size += bp->size;
p->next = bp->next;
} else
p->next = bp;
freep = p;

p bp ap p->next

5
8 3 7 5
Free(): Above and Below Free
/* Look to see if we're adjacent to the block after the freed block */
if ((char *)bp + bp->size * Headersize == p->next) {
bp->size += p->next->size;
bp->next = p->next->next;
} else
bp->next = p->next;

/* Look to see if we're adjacent to the block before the freed block */
if ((char *)p + p->size * Headersize == bp) {
/* add the freed block to the block at p */
p->size += bp->size;
p->next = bp->next;
} else
p->next = bp;
freep = p;

p
bp ap p->next

15 10
5 3 7 5
Section of morecore() Code 2
/* Don't ask for any less than NALLOC */
if (nu < NALLOC)
nu = NALLOC;
cp = sbrk(nu * Headersize);
if (cp == (char *) -1) /* no space */ Stack
return NULL;

/* Take the new block, add a header, and free() it */


up = (Header *) cp;
up->size = nu;
_free((char *)up + Headersize);
return freep; Add’l Heap
Heap
• sbrk() is a Linux system call to obtain more memory.
• It works at the end of the data segment/heap (a.k.a. “the
break”) to acquire at least n bytes more space from the OS Data
• It returns an aligned pointer to the new space or -1 if no
return
space is available Code pointer from
• The -1 is an odd historical artifact sbrk()
Question

After the execution of this code,


double *mp = &d[3];
char *xp = (char *)mp + 16;
to what memory address does xp point?

A. 0x10 bytes past the address of d[3]

B. 10 bytes past the beginning of d

C. 10 bytes before the beginning of d

D. 16 bytes before the beginning of d

Today's number is
16,661
Memory Management
 What are the equivalents of malloc and free in Java? Python?

 How can we “automatically” collect garbage?


 Mark & Sweep Techniques
 Reference Counting Techniques
 And many, many others...

You might also like