Thanks to visit codestin.com
Credit goes to www.scribd.com

0% found this document useful (0 votes)
10 views46 pages

Memory Management v2

The document covers memory management in C programming, including the C memory model, heap usage, and memory allocation functions like malloc, calloc, and free. It discusses static and stack allocation, memory structure, and common memory allocation problems such as memory leaks and fragmentation. Additionally, it touches on endianness and how data is stored in memory, highlighting the differences between big-endian and little-endian systems.

Uploaded by

engr.ahmaddaud
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)
10 views46 pages

Memory Management v2

The document covers memory management in C programming, including the C memory model, heap usage, and memory allocation functions like malloc, calloc, and free. It discusses static and stack allocation, memory structure, and common memory allocation problems such as memory leaks and fragmentation. Additionally, it touches on endianness and how data is stored in memory, highlighting the differences between big-endian and little-endian systems.

Uploaded by

engr.ahmaddaud
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/ 46

Week 2

C Programming
Memory Management
Agenda
• C Memory model
• Heap usage
• C Demos
• Endianness

Memory Management 2
Agenda
• C Memory model

• Heap usage

• Endianness

Memory Management 3
Memory: the C Story
C offers a story both simpler and more complex than Java
Memory is a sequence of bytes, read/written by providing an
address
Addresses are values manipulated using arithmetic & logic
operations
Memory can be allocated:
‣ Statically
‣ on the stack
‣ Dynamically on the heap

Heap

0x1000434
Memory Management 4
Static and Stack allocation
•Static allocation #include <unistd.h>
#include <stdio.h>
with the keyword
static static int sx;
static int sa[100];
•Stack allocation static int sy;
automatic by the
int main() {
compiler for local
int lx;
variables static
•printf can int
printf("%p\n", &sx);
0x100001084
sz;
display the printf("%p\n", &sa);
0x1000010a0
printf("%p\n", &sy);
0x100001230
address of any printf("%p\n", &lx);
0x7fff5fbff58
identifier c
printf("%p\n", &sz); 0x100001080
printf("%p\n", &main); 0x100000dfc

Memory Management 5
Static and Stack allocation
Any value can
be turned into a static int sx;
pointer static int sa[100]
;
static int sy;

Arithmetics on int main() {


for(p= (int*)0x100001084;
pointers allowed p <=
(int*)0x100001230; p++)
{
*p = 42;
Nothing prevents }
a program from printf("%i\n",sx); 42
printf("%i\n",sa[0]); 42
writing all over
printf("%i\n",sa[1]); 42
memory

Memory Management 6
Memory Structure
• Main memory can be thought of as a large 0xFFFF...FFF
array of bytes.
• When running a C program, memory
Stack
assignments largely fall under four distinct
"chunks":
• Stack
• Heap
Heap
• Static/data
• Text/Code Static/data
• Generally speaking, you don't use all 232 bytes;
if you try to access a random memory address Code/text
you didn't get assigned, your code will likely
crash. This is known as a segmentation fault
0x0000...000
Memory Management 7
Memory Structure
• Code/text 0xFFFF...FFF
• One of the key advancements in CS was to design
programs as software instead of hardware.
• Instead of messing with wires to program a
computer, you can just write your program as data
• The code segment stores the actual bytecode that
comprises your program.
• Fixed size, ideally never changed after loading the
program
• Includes some constants!
• Constants that are considered "built-in" to the
code
• Ex. x = y + 1; the 1 is part of the code.

0x0000...000
Memory Management 8
Memory Structure
● Static/data 0xFFFF...FFF
○ Main idea: It's possible to do some
analysis before the code starts running
to determine a set of variables that must
be allocated space. The data segment
contains as many of those variables as
possible.
○ Fixed size, for efficiency. Two main data
values:
○ Global variables (you only ever need one
copy of a global variable, so fixed size)
○ String literals (you can determine how
many string literals there are in a
program)
0x0000...000
Memory Management 9
Memory Structure
● Stack 0xFFFF...FFF
○ Every function call sets aside some space on the
stack for its local variables (plus some metadata)
○ Designed for temporary storage; after a function Stack pointer
returns, all data in that function's stack frame (sp)
gets freed
○ Variable sizeof the stack; grows downward as you
call functions and shrinks as you return from
functions
○ Stack frame has
○ Locals
○ Parameters to function
○ Return address
○ Static link
○ Dynamic link
○ SP is used to access variables in stack frame 0x0000...000
Memory Management 10
Memory Structure
• Heap 0xFFFF...FFF
• For any variables that need to persist
across functions, the heap is available Stack pointer
• In C, all heap memory must be
(sp)
manually allocated; a C program won't
allocate heap space unless you tell it
to explicitly.
• Similarly, heap memory is only freed
when manually requested; if you
forget to free something, it takes up Heap pointer
heap space forever (hp)
• Grows upward as memory is allocated
• Often the source of a lot of bugs when
starting off working with C. 0x0000...000
Memory Management 11
Agenda
• C Memory model

• Heap usage

• C Demos

• Endianness

Memory Management 12
Heap Usage
• The heap is run by a memory manager, which is part of the
operating system (or OS)
• In order to use the heap, our program needs to request
memory from the OS, and promises to return that memory
when it's no longer needed.

Memory Management 13
Dynamic memory management
#include <stdlib.h>

void* malloc(size_t s)
void* calloc(size_t n, size_t s)
void free(void* p)
void* realloc(void* p, size_t s)

Allocate and free dynamic memory

Memory Management 14
malloc(size_t s)

Allocates s bytes and returns a pointer to the allocated memory.


Memory is not cleared
Returned value is a pointer to alloc’d memory or NULL if the request
fails
You must cast the returned pointer
• It is a void* Return a void*

p = (char*) malloc(10); /* allocated 10


bytes */
if(p == NULL) { /*panic*/ }

CAN FAIL, CHECK THE RETURNED POINTER NOT NULL

Memory Management 15
malloc(size_t s)

Memory Management 16
calloc(size_t n, size_t s)
Allocates memory for an array of n elements of s bytes each
and returns a pointer to the allocated memory.
The memory is set to zero
The value returned is a pointer to the allocated memory or
NULL

p = (char*) calloc(10,1); /*alloc 10


bytes */
if(p == NULL) { /* panic */ }

CAN FAIL, CHECK THE RETURNED POINTER NOT NULL

What’s the difference between


Memory array[10] and calloc(10,4)
intManagement 17
calloc(size_t n, size_t s)

Memory Management 18
free(void* p)
Frees the memory space pointed to by p, which must have been
allocated with a previous call to malloc, calloc or realloc
If memory was not allocated before, or if free(p) has
already been called before, undefined behavior occurs.
If p is NULL, no operation is performed.
free() returns nothing

char *mess = NULL;


mess = (char*) malloc(100);

free(mess); *mess = 43;
FREE DOES NOT SET THE POINTER TO NULL

Memory Management 19
free()

Memory Management 20
realloc(void* p,size_t s)
Changes the size of the memory block pointed to by p to s
bytes

Contents unchanged to the minimum of old and new sizes

Newly alloc’d memory is uninitialized.

Returns pointer to alloc’d memory, may be different from p, or


NULL if the request fails or if s==0

If fails, original block left untouched, i.e. it is not freed or moved

Memory Management 21
realloc(void* p,size_t s)

Memory Management 22
memcpy(void*dest,const void*src,size_tn)
•Copies n bytes from src to dest
•Returns dest
•Does not check for overflow on copy

char buf[100];
char src[20] = “Hi there!”;
int type = 9;
memcpy(buf, &type, sizeof(int));/* copy an
int*/
memcpy(buf+sizeof(int), src, 10); /*copy 10 chars*/

Memory Management 23
memset(void *s, int c, size_t n)
Sets the first n bytes in s to the value of
c
‣ (c is converted to an unsigned char)

Returns s
Does not check for overflow

memset(mess, 0, 100);

Memory Management 24
Heap Example
int* i = (int*)malloc(sizeof(int) * 5);
for(int j = 0; j < 5; j++)

i[j] = j;
int* k = (int*)calloc(3, sizeof(int));
i = (int*) realloc(i, sizeof(int)*2);
i = (int*) realloc(i, sizeof(int)*10);
free(k);
k = (int*)malloc(sizeof(int)*6);
for(int j = 0; j < 5; j++) k[j] = j;
free(i);
free(k);

Memory Management 25
Best practices for memory use
• Always free the dynamic memory
• free() is not recursive
• Define constructors and destructor for complex data structures
• Memory in stack frame is reclaimed upon function return
• Heap has fragmentation problem
• Uses linked list to store data
• Heap allocation and access is slow
• As compared to stack
• Stack memory is limited
• Uses LIFO to store data

Memory Management 26
Memory Allocation Problems
Memory leaks
‣ Alloc’d memory not freed appropriately
‣ If your program runs a long time, it will run out of
memory or slow down the system
‣ Always add the free on all control flow paths after a
malloc

void *ptr = malloc(size);


/*the buffer needs to double*/
size *= 2;
ptr = realloc(ptr, size); if
(ptr==NULL)
/*realloc failed, original address in
ptr lost; a leak has occurred*/
return 1; Memory Management 27
Memory Allocation Problems

Use after free


‣ Using dealloc’d data
‣ Deallocating something twice
‣ Deallocating something that was not allocated
• Can cause unexpected behavior. For example, malloc
can fail if “dead” memory is not freed.
• More insidiously, freeing a region that wasn’t malloc’ed or
freeing a region that is still being referenced

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


free(ptr);
*ptr = 7; /* Undefined behavior
*/
Memory Management 28
Memory Allocation Problems
Memory overrun
‣ Write in memory that was not allocated
‣ The program will exit with segmentation fault
‣ Overwrite memory: unexpected behavior

int* y= …
int* x= y+10;
for(int* p= x; p >=y;
p++)
{
*p = 42;
}
Memory Management 29
Memory Allocation Problems
Fragmentation
‣ The system may have enough memory but
not in contiguous region

int* vals[10000];

int i;
for (i = 0; i < 10000; i++)
vals[i] = (int*)
malloc(sizeof(int*));

for (i = 0; i <
10000; i = i
+ 2) free(vals[i]);
Memory Management 30
Checklist
• NULL pointer at declaration
• Verify malloc succeeded
• Initialize alloc’d memory
• free when you malloc
• NULL pointer after free
• Do not use memory after free

Memory Management 31
Agenda
• C Memory model

• Heap usage

• Endianness

Memory Management 32
malloc vs calloc
• Which is faster? How much? Why?

Memory Management 33
Memory Leak
• What is memory leak?
• What are best practices for memory management in C?

Memory Management 34
Uninitialized Memory
• What happens when you try to access uninitialized memory?
• What happens when you access memory after free()?

Memory Management 35
Agenda
• C Memory model

• Heap usage

• C Demos

• Endianness

Memory Management 36
Endianness
• So far, we’ve discussed how we store values in binary
• Ex. We write int i[] = {0x6472 6167, 0x7320 6E65, 0x7400 646F}.
• If we assume &i == 0xF000 0000, then our memory would look something like this:
Address (Last hex digit) 0 1 2 3 4 5 6 7 8 9 A B
0x
74
• If we do i[1], the compiler adds 0xF000 0x64726167
0000 + 10x73206E65 00 =
* sizeof(int)
64
0xF000 0004, then takes the four bytes starting from that address as an integer. This
Value 6F
corresponds to 0x73206E65, so it works.
• What happens if we do ((char*) i)[2]?
• Note: These slides follow a convention of using a new 0x prefix for every array
element. Thus, 0x64726167 0x73206E65 0x7400646F is an array of 32-bit values,
while 0x64 0x72 0x61 0x67 is an array of 8-bit values.
Memory Management 37
Endianness
• So far, we’ve discussed how we store values in binary
• Ex. We write int i[] = {0x6472 6167, 0x7320 6E65, 0x7400 646F}.
• If we assume &i == 0xF000 0000, then our memory would look something like this:

Address (Last hex digit) 0 1 2 3 4 5 6 7 8 9 A B


Value 0x64726167 0x73206E65 0x7400646F
Value ? ? ? ? ? ? ?? ?? ??
• If we do ((char*) i)[2], the compiler adds 0xF000 0000 + 2 * sizeof(char) =
0xF000 0002, then takes the one byte starting from that address as a char.
This yields something (since there is data there), but what it yields depends
on how the subbytes of our 4-byte int get stored in memory. The way things
get stored is known as the endianness of the system.

Memory Management 38
Big-Endian
• In a big-endian system, we write the Most Significant Byte “first” (that
is, in the lower address.
Address (Last hex digit) 0 1 2 3 4 5 6 7 8 9 A B
Value 0x64726167 0x73206E65 0x7400646F
Value 0x6E 0x65 0x74
0x64 0x72 0x61 0x67 0x73 0x20 0x00 0x64 0x6F

• If we do ((char*) i)[2], the compiler adds 0xF000 0000 + 2 *


sizeof(char) = 0xF000 0002, then takes the one byte starting from
that address as a char. This yields 0x61.
• If we do printf((char*) i); we would interpret this block of memory as
a character array, so we’d get 0x64 0x72 0x61 …, which when
converted to ASCII yields “drags net”
Memory Management 39
Little-Endian
• In a little-endian system, we write the Least Significant Byte “first”
(that is, in the lower address.
Address (Last hex digit) 0 1 2 3 4 5 6 7 8 9 A B
Value 0x64726167 0x73206E65 0x7400646F
Value 0x20 0x73 0x6F0
0x67 0x61 0x72 0x64 0x65 0x6E 0x64 0x00 x74

• If we do ((char*) i)[2], the compiler adds 0xF000 0000 + 2 *


sizeof(char) = 0xF000 0002, then takes the one byte starting from
that address as a char. This yields 0x72.
• If we do printf((char*) i); we would interpret this block of memory as
a character array, so we’d get 0x67 0x61 0x72 …, which when
converted to ASCII yields “garden sod”
Memory Management 40
Endianness
• Note: In either case, if we read i[1], we end up with our original integer. This is a good
checksum to confirm your understanding; if you write a number, you should be able to
read that number to get the same thing.

Address (Last hex digit) 0 1 2 3 4 5 6 7 8 9 A B


Value 0x64726167 0x73206E65 0x7400646F
Value (Big Endian) 0x64 0x72 0x61 0x67 0x73 0x20 0x6E 0x65 0x74 0x00 0x64 0x6F

Value (Little Endian) 0x20 0x73 0x6F0


0x67 0x61 0x72 0x64 0x65 0x6E 0x64 0x00 x74

• In Big Endian: the compiler adds 0xF000 0000 + 1 * sizeof(int) =


0xF000 0004, then takes the four bytes starting from that address: 0x73 0x20 0x6E
0x65. Since this is big-endian, we combine them with the first address being the MSB,
so we get 0x73206E65, which is the correct result
Memory Management 41
Endianness
• Note: In either case, if we read i[1], we end up with our original integer. This is
a good checksum to confirm your understanding; if you write a number, you
should be able to read that number to get the same thing.
Address (Last hex digit) 0 1 2 3 4 5 6 7 8 9 A B
Value 0x64726167 0x73206E65 0x7400646F
Value (Big Endian) 0x64 0x72 0x61 0x67 0x73 0x20 0x6E 0x65 0x74 0x00 0x64 0x6F

Value (Little Endian) 0x20 0x73 0x6F0


0x67 0x61 0x72 0x64 0x65 0x6E 0x64 0x00 x74

• In Little Endian: the compiler adds 0xF000 0000 + 1 * sizeof(int) =


0xF000 0004, then takes the four bytes starting from that address: 0x65 0x6E
0x20 0x73. Since this is big-endian, we combine them with the first address
being the LSB, so we get 0x73206E65, which is the correct result

Memory Management 42
Endianness
• All endiannesses work as long as you’re consistent. It only
affects cases where you either transfer to a new endianness
(different systems can use different endiannesses), or when
you split/merge a block of data into smaller/larger chunks.
• This comes up a lot when working with unions, since a union is
designed to interpret the same block of data in different ways.
• Officially, C defines this to be undefined behavior; you’re not
supposed to do this.
• With unions, you’re supposed to use that block as only one of the
components

Memory Management 43
Endianness
• Big Endian is commonly used in networks (e.g. communicating
data between computers).
• Little Endian is commonly used within the computer
• The default endianness for C, RISC-V, x86, etc. In general, you’ll be
working with little-endian systems when programming

Memory Management 44
Endianness
• Some systems are bi-endian (allows you to switch endianness
if you want)
• There are mild benefits for either endianness (one might let you
read important data faster than the other), but ultimately, we
use different endiannesses because computer programmers
are bad at coordinating standards, and once a standard is
made, backwards compatibility is an issue.
• The hton and ntoh function sets convert from host endianness
to network endianness and vice versa.

Memory Management 45
Thank You

You might also like