8 CPS393 MemoryC

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 103

C/CPS 393

Introduction to UNIX, C and C++


Prof. Alex Ufkes

Topic 8: Memory management in C


Notice!

Obligatory copyright notice in the age of digital


delivery and online classrooms:

The copyright to this original work is held by Alex Ufkes. Students


registered in course C/CPS 393 can use this material for the purposes
of this course but no other use is permitted, and there can be no sale
or transfer or use of the work for any other purpose without explicit
permission of Alex Ufkes.

© Alex Ufkes, 2022 2


Course Administration

Midterm #2, covering all Linux material,


takes place in your lab on Nov 2/3

© Alex Ufkes, 2022 3


Today

Memory Management in C:
Static arrays, strings
Pointers, addresses
Dynamic allocation
Memory (un)safety in C

© Alex Ufkes, 2022 4


© Alex Ufkes, 2022 5
Recall: Variables in Memory

char c; /* 1 byte allocated */


int wholeNum1; /* 4 bytes allocated */
double realNum2; /* 8 bytes allocated */

c wholeNum1 realNum2

1 byte 4 bytes 8 bytes

© Alex Ufkes, 2020, 2021 7


Recall: Use & to get address

The & operator returns the address of a variable

int number
scanf( , &number);

© Alex Ufkes, 2020, 2021 8


Pointer Data Types

A pointer is a variable that stores the address of another variable:

char *cptr; /* stores the address of a char */


int *iptr; /* stores the address of an int */
float *fptr; /* stores the address of a float */
double *dptr /* stores the address of a double */

The * is used in the variable declaration to specify a pointer.


char *cptr; /* stores the address of a char */
int *iptr; /* stores the address of an int */
float *fptr; /* stores the address of a float */
double *dptr /* stores the address of a double */

In a 32-bit application, pointers are also 32 bits (4 bytes), regardless


of the data type they are pointing to.

char* is 4 bytes, even though it points to a char, which is 1 byte.

The value of a pointer is simply a memory location (address)


char abc = 'A';
char *xyz = &abc;
printf("Value of abc: %c\n", abc);
printf("Address of abc: %d\n", &abc);
printf("Value of xyz: %d\n", xyz);
printf("Address of xyz: %d\n", &xyz);

xyz (4 bytes) abc (1 byte)

6356771 Memory

6356764 6356771
Dereferencing
* Operator

NOT multiplication! - Different in the context of pointers.


Use * to dereference a pointer.

Primitive data types (int, char, float, double) CANNOT be dereferenced


an
C1 = A pti points to

- A
CI
c2 A
char
=

char *ptr = &c1;


-
// ptr stores the address of c1
c2 = *ptr; // dereference ptr, store in c2
*ptr
points at cl // pointed to by ptr
pointer
stone a in cl

printf( \ , c1); /* c1 is: C */


printf( ptr is: %c\ , *xyz); /* *ptr is: C */
printf( \ , c2); /* c2 is: A */

ptr c1 c2

&c1 Memory
int i, j, *p, *q; // pointers and ints together
p = &i; p=
adress of i
// value of p is the address of i
q = &j; 9
= adress of j // value of q is the address of j
*p = 5; put
s where

i S=
points at
// store 5 at the address stored in p
*q = *p + i; // dereference p, add i, store at
u

9point
at what
+
1

is the
5 +

+
5

what is the value


// address stored in q
value of wheres

8
=
j
of
10
points S

printf( i = %d, j = %d\ , i, j);


printf( i = %d, j = %d\ , *p, *q);

Output? i = 5, j = 10
int i, j, *p, *q; // pointers and ints together
p = &i; // value of p is the address of i
q = &j; // value of q is the address of j
*p = 5; // store 5 at the address in p
*q = *p + i; // dereference p, add i, store at
// address in q
printf( i = %d, j = %d\ , i, j);

5 10
You Need Help!

More
Pointer
Examples
int x = 57, y = 0;
int *a = &y, *b = &x;
address of
address of x

*a = 12; y 12
=

// y = 12
b = a; ↓ and a both point
to
// b and a both point to y
y

*b = 15; y
15
=
// y = 15

printf( \n", x, y);

x = 57, y = 15
int x = 57, y = 0;
int *a, *b = &y; ↑
-
points

the address
of

*b = 7; y
7
=

// y = 7
a = &x; a = adness of x
// a points to x
x = *a - *b; // x = x y
the value the value
-

where a
points o *b points
X -
7 Sp
SF
- =

printf( \n", x, y);

x = 50, y = 7
int x, y, z, *p1, *p2, *p3, *p4;
the X
address of

p1 = &x; // p1 points to x
p4 = p1; points
pup)
// at X
p4 points to x
p2 = p4; p1cPEIEx // p2 points to x
*p4 = 5; // x
G
=

x = 5
y = x; y //
= 5
y = 5

printf( \n", x, y);

x = 5, y = 5
char c1, c2, c3, *ptr;

ptr = &c1; // value of ptr is the address of c1


*ptr // dereference ptr
C1 = A

ptr = &c2; // value of ptr is the address of c2


*ptr // dereference ptr
c B
=

ptr = &c3; // value of ptr is the address of c3


*ptr // dereference ptr
23 =

ptr c1 c2 c3

&c1 &c2 &c3


Is This OK?

int x, *p1;
p1 = &x;
printf("Please enter an integer: ");
scanf("%d", p1); /* Missing & */
printf("x = %d\n", x);

Yes! scanf needs an address, p1 is an address


Consider the following: char
sign
+
double
int
num
whole
3.14159 3
double
Problem: fraction
can only return one thing! 0.14159
Pointers as
char parameters!
*sign

double int
num whole

double Single return value:


*fraction return whole;
int split (double num, char *sign, double *fraction)
{
Pointers
int whole = abs((int)num);
*fraction = fabs(num) whole;
if (num >= 0)

else De-reference pointers


- to assign values
return (whole);
}
int split (double num, char *sign, double *fraction)
{ /*split code*/
}
int main (void)
{
double f, n = 3.1415;
When we de-reference
char s;
sign and fraction in
int w = split(n, &s, &f); split, we are accessing
printf( \ , s); s and f in main!
printf( \ , w);
printf( \ , f);
return (0);
}
int split (double num, double *fr, char *sign) Memory
{ f 800
int whole = abs((int)num); w 808
*fr = fabs(num) whole; s 812
if (num >= 0)
else -
return (whole);
} num 3.14159 912
int main (void)
fr 800 920
{ sign 812 924
double f; int w; char s;
w = split(3.14159, &f, &s);
printf( , s, w, f);
return (0);
}
int split (double num, double *fr, char *sign) Memory
{ f 0.14159 800
int whole = abs((int)num); /* 3 */ w 3 808
*fr = fabs(num) whole; /* 0.14159 */ s 812
if (num >= 0)
else -
return (whole);
} num 3.14159 912
int main (void)
fr 800 920
{ sign 812 924
double f; int w; char s; whole 3 928
w = split(3.14159, &f, &s);
printf( , s, w, f);
return (0);
}
Quadratic Formula

void quadForm (double a, double b, double c,


double *x1, double *x2)
{
double tmp = sqrt(b*b 4*a*c);

*x1 = (-b + tmp)/(2*a);


*x2 = (-b - tmp)/(2*a);
}
Quadratic Formula

void quadForm (double a, double b, double c,


double *x1, double *x2);
int main (void)
{
double a = 1.1, b = 7.8, c = 2.5;
double root1, root2;
quadForm(a, b, c, &root1, &root2);
printf( , root1, root2);
return 0;
}
char topic[] = ;

© Alex Ufkes, 2022 33


Arrays

An array is a sequence of values of the same type:

#include <stdio.h>
int main()
{
int nums[100]; An array of 100 integers
return 0;
}

© Alex Ufkes, 2020, 2021 34


Array Syntax

Number of
type array_name [length];
elements

Must be legal identifier

© Alex Ufkes, 2020, 2021 35


#include <stdio.h>
int main()
{
int nums[5];
return 0;
}
number of elements

Memory

size of each element


© Alex Ufkes, 2020, 2021 36
#include <stdio.h>
int main()
{
int nums[5] = {2, 4, 6, 8, 10};
return 0;
}

2 4 6 8 10 Memory

© Alex Ufkes, 2020, 2021 37


#include <stdio.h>
int main()
{
int nums[] = {2, 4, 6, 8, 10};
return 0;
}
Size will be equal to the number of initialized elements.

2 4 6 8 10 Memory

© Alex Ufkes, 2020, 2021 38


#include <stdio.h>
int main()
{
int nums[5]; subscript/offset/index
nums[0] = 17; /* first element */
nums[1] = -3; /* second element */
nums[2] = 0; /* third element */
nums[3] = 57; /* fourth element */
nums[4] = 3; /* fifth element */

return 0;
}
The usual, C indexing starts at 0
© Alex Ufkes, 2020, 2021 39
#include <stdio.h>
int main()
{
int nums[5];
Address of first element
scanf( , &nums[0]);
scanf( , &nums[1]); Elements of an integer
scanf( , &nums[2]); array can be treated like
scanf( , &nums[3]); any other integer!
scanf( , &nums[4]);

return 0;
}
© Alex Ufkes, 2020, 2021 40
IMPORTANT!

If the array has 5 elements, the valid indexes are 0, 1, 2, 3, 4

#include <stdio.h>
int main() This will compile but may
{ crash during run-time.
int nums[5];
nums[5] = 57; /* BAD! Out of bounds! */
return 0;
}

© Alex Ufkes, 2020, 2021 41


IMPORTANT!

#include <stdio.h>
int main()
{
int nums[5];
nums[5] = 57; /* BAD! Out of bounds! */
return 0;
}

57 Memory
0 1
© Alex Ufkes, 2020, 2021 2 3 4 42
We can overrun the array by
999*4 bytes and still be in within

© Alex Ufkes, 2020, 2022 43


Here, we attempt to access memory
not owned by our program.
Dreaded segfault, program terminates

© Alex Ufkes, 2020, 2022 44


More Array Rules & Properties

Size can be variable (in newer versions of C):

int x; Declaration
printf( );
Other statements
scanf( , &x);
int nums[x]; OK! Another Declaration
return 0;

© Alex Ufkes, 2020, 2021 45


More Array Rules & Properties

Once the size is set, it cannot be changed

int x, nums[100];
nums = nums[200]; BAD!
return 0;

Once declared, nums is stuck having 100 elements.


Arrays declared with variable length are still fixed in size

© Alex Ufkes, 2020, 2021 46


Arrays, Pointers, Addresses

Array name without an int nums[5];


index is the address of nums[3] = 7
the first element.
*nums = 2*nums[3];
In other words: /* is the same as: */
&nums[0] == nums nums[0] = 2*nums[3];

0 1 2 3 4

14 7 Memory
Pointer Arithmetic

These loops do the same thing!

int i, nums[5]; int i, nums[5];


for (i = 0; i < 5; i++) for (i = 0; i < 5; i++)
{ {
nums[i] = i*i; *(nums + i) = i*i;
} } Address of first element

nums == &nums[0]
nums is the address of the first element.
nums + i == &nums[i]
*(nums + i) == nums[i] nums+i is the address of the ith element.
Addresses go up
by 4. Why?
Pointer Shenanigans

In C, pointer arithmetic can be used to completely bypass the type system:

the first 2 bytes of an int as a short.


This can be done with any two types.
We can read the rightmost 4 bytes of a
double as an int, etc.
We can treat memory any way we want

© Alex Ufkes, 2020, 2022 51


Arrays & Functions

Write a user-defined void function that does the following:


Print all the elements in an array.

© Alex Ufkes, 2020, 2021 52


#include <stdio.h>
Why do we pass the size of
void print_array(int arr[], int size) the array into the function?
{
int i;
for(i = 0; i < size; i++)
printf( , arr[i]);
}
int main (void)
{
int nums[5] = {1, 2, 3, 4, 5};
print_array(nums, 5);
return 0;
If we change the size of the array, only
}
© Alex Ufkes, 2020, 2021
the main() function has to be modified. 53
Arrays & Functions

Write a user-defined function that does the following:


Finds the largest element in an array and returns it.

© Alex Ufkes, 2020, 2021 54


#include <stdio.h>
int find(int arr[], int size)
{
int i, largest = arr[0]; Initialize largest to the first element
for (i = 1; i < size; i++)
{
if (arr[i] > largest) Compare each element to largest. If arr[i]
largest = arr[i]; is bigger than largest, set largest to arr[i]
}
return(largest); Return largest
}
int main (void)
{
int nums[6] = {1, -2, 0, 4, -9, 3};
printf( , find(nums, 6));
return (0);
}
© Alex Ufkes, 2020, 2021 55
Array Length with sizeof()

Returns the size, in bytes, of a given data type.

© Alex Ufkes, 2020, 2021 56


Array Length with sizeof()

Turns out it works on arrays, too!

© Alex Ufkes, 2020, 2021 57


Array Length with sizeof()

CAREFUL: It only works for static arrays declared in the same scope!

Array is passed as int*,


pointer to first element.
We get sizeof(int*)

© Alex Ufkes, 2020, 2021 58


Arrays & Functions

Write a user-defined function that does the following:


Takes an array and creates a second array whose elements
are each three times bigger than the corresponding elements
in the original array. Return the tripled array.

© Alex Ufkes, 2020, 2021 59


Returning (Static) Arrays

Static arrays can never be returned by a function.

int arr[5] = {1, 2, 3, 4, 5};


return(arr);

Why?
© Alex Ufkes, 2020, 2021 60
No! Will not compile!

© Alex Ufkes, 2022 61


Nor will this!

© Alex Ufkes, 2022 62


A static array, declared in a function, is
local to that function.
Destroyed when the function returns!

© Alex Ufkes, 2022 63


Arrays & Functions

Write a user-defined function that does the following:


Takes an array and creates a second array whose elements
are each three times bigger than the corresponding
elements in the original array. Return the tripled array.

One option?

© Alex Ufkes, 2020, 2021 64


#include <stdio.h>
void triple(int arr[], int arr3[], int size)
{
int i;
for (i = 0; i < size; i++) arr is the address of the first element of nums.
arr3[i] = arr[i]*3; arr3 is the address of the first element of nums3.
}
int main (void)
{
int i, nums3[5], nums[5] = {1, 2, 3, 4, 5};
triple(nums, nums3, 5);
printf( \ );
for (i = 0; i < size; i++) nums and nums3 are declared in main()
printf( , nums[i]); Their addresses get passed into triple()
printf( \ );
for (i = 0; i < size; i++)
printf( , nums3[i]);
return (0);
}
© Alex Ufkes, 2020, 2021 65
#include <stdio.h> Memory
void triple(int arr[], int arr3[], int size) 1
{
nums 812
int i; 2 816
for (i = 0; i < size; i++) 3 820
arr3[i] = arr[i]*3;
} 4 824
int main (void) 5 828
{ nums3 3 832
int i, nums3[5], nums[5] = {1, 2, 3, 4, 5};
triple(nums, nums3, 5); 6 836
printf( \ );
9 840
for (i = 0; i < size; i++) 12 844
printf( , nums[i]);
printf( \ );
15 848
for (i = 0; i < size; i++) arr 812 852
printf( , nums3[i]); arr3 832 856
return (0);
} size 5 860
© Alex Ufkes, 2020, 2021 66
© Alex Ufkes, 2020, 2021 67
Arrays & Functions

Write a user-defined function that does the following:


Takes an array and creates a second array whose elements
are each three times bigger than the corresponding
elements in the original array. Return the tripled array.

Another option? Return a new dynamically allocated array.

© Alex Ufkes, 2020, 2021 68


Dynamic VS Static Allocation

Static arrays:
Stored on the stack, meaning size is limited (varies by compiler/OS)
Destroyed when they leave scope
Declare using int arr[], double arr[] etc.

Dynamic arrays:

Declare using pointers: int*, double*, etc. and malloc/calloc

© Alex Ufkes, 2022 69


malloc & calloc

Functions for allocating memory. Defined in stdlib.h

int size = 50;


double *arr1; /* pointer to reference our array */

arr1 = (double*) calloc(size, sizeof(double));

Return value of calloc is void* by Number of Size of each


default. We must use typecasting to elements element
match the type of our array.

arr1 can now be used as if it were a statically declared array.


malloc & calloc

arr1 can now be used as if it were a statically declared array:

int size = 50;


double *arr1;

arr1 = (double*) calloc(size, sizeof(double));


arr1[0] = 34.6;
arr1[1] = -0.077; // elements can be accessed
arr1[2] = arr[1] + 5; // and assigned in exactly
arr1[3] = 3.14159; // the same way
malloc?

Only takes one argument, and elements are not set to 0

int size = 50;


double *arr1; /* pointer to reference our array */

arr1 = (double*) malloc(size*sizeof(double));

Like calloc, return value Total number of


of malloc is void*. bytes to allocate

arr1 can now be used as if it were a statically declared array.


malloc & calloc

Arrays allocated using calloc are initialized to 0


Arrays allocated using calloc must be freed
manually using free()

until our program terminates.


C does NOT use garbage collection!

calloc is much faster than malloc, for reasons that are fascinating
but beyond the scope of this course, and touch on CPS590 topics:

https://vorpus.org/blog/why-does-calloc-exist/
#include <stdio.h>
#include <stdlib.h>
int main () Is there a problem here?
{ What values are stored in the array?
int size, i;
calloc initializes the reserved
double *arr;
memory to zero. This is safe.
printf( );
scanf( , &size);
arr = (double*) calloc(size, sizeof(double));
for (i = 0; i < size; i++)
printf( , arr[i]);
Dynamically allocated memory
free(arr); must always be freed!
}

Console:
Each iteration, we allocate 1-million doubles.
Doubles from previous allocation are still on
the heap! Orphaned.
Very bad, no way to free at that point.
© Alex Ufkes, 2022 75
Dynamic arrays can be
returned from functions!
Remember to free them.

© Alex Ufkes, 2022 76


© Alex Ufkes, 2022 77
© Alex Ufkes, 2020, 2021 78
Strings

A string is a character array that is terminated by the null character \

#include <stdio.h>
int main()
{
char city[] = { , , , , , , , \ };
printf( \ \ /* 0 on the asci table */
return 0;
}

© Alex Ufkes, 2020, 2021 79


String Initialization

#include <stdio.h>
int main()
{ What is the size of city?
char city[] = ;
return 0;
}
When we initialize this way, the null character is
automatically added to the end of the char array.

String size equals the number of characters plus one.

© Alex Ufkes, 2020, 2021 80


Printing Strings

#include <stdio.h>
int main() Placeholder for string
{
char city[] = ;
printf( , city);
puts(city); puts is for strings only. Inserts a
return 0; newline after printing the string.
}
© Alex Ufkes, 2020, 2021 81
How Does printf

printf( , city);

city is just an address, like any other array.

© Alex Ufkes, 2020, 2021 82


When we print a string using the %s placeholder, it tells the system to
keep printing characters until we hit the null character, \

#include <stdio.h>
int main()
{
char city1[] = { , i , , , \ };
char city2[] = { , , , };
printf( , city1);
printf( , city2); No null character!
return 0; What happens?
}
© Alex Ufkes, 2020, 2021 83
We get unpredictable junk, because the
system keeps reading characters until we
hit \ somewhere else.
© Alex Ufkes, 2020, 2021 84
#include <stdio.h>
int main()
{
char city[] = ;

Shell:
printf( , city); Taranta
return 0;
}
© Alex Ufkes, 2020, 2021 85
int i;
char city[] = ;

for (i = 0; i < 7; i++) Loop through each


{ character in string
if (city[i
{
city[i Replace with
}
}
Shell:
printf( , city); Taranta
© Alex Ufkes, 2020, 2021 86
int i;
char city[] = ;
Even better!
Go until we hit the
for (i = 0; city[i \ i++)
null character.
{
Works on any string
if (city[i
{
city[i
}
}
Shell:
printf( , city); Taranta
© Alex Ufkes, 2020, 2021 87
© Alex Ufkes, 2020, 2021 88
Strings as Input

char city[10];
scanf( , city);

Array name alone is an ADDRESS

scanf( , &city[0]); /* Equivalent */


© Alex Ufkes, 2020, 2021 89
char city[16]; We only scan one string
scanf( , city);
printf( , city);

Console
North Bay Treated as two separate strings
North
© Alex Ufkes, 2020, 2021 90
© Alex Ufkes, 2020, 2021 91
char city[16];
gets(city);
printf( , city);

Newline character is considered a delimiter

Console
North Bay
North Bay
© Alex Ufkes, 2020, 2021 92
char city[16]; What if the user enters string
gets(city); longer than 15 characters?
15 + \ = 16 total
printf( , city);

We will overrun the bounds of our array!


Just like integer arrays, double arrays, etc.
Be sure to declare a char array long enough
to fit the input.

© Alex Ufkes, 2020, 2021 93


© Alex Ufkes, 2020, 2021 94
T o r o n t o \0
6356746 (city2)
© Alex Ufkes, 2020, 2021 6356748 (city1) 95
New Y o r k \0 \0
6356746 (city2)
© Alex Ufkes, 2020, 2021 6356748 (city1) 96
String Functions

#include <string.h>
char city[] = ;
int length = strlen(city);
printf( \ , length);

Console Length does not include


Length: 4 the null character!
© Alex Ufkes, 2020, 2021 97
String Copy

char city1[8] = ;
char city2[8];
city2 = city1;

Use strcpy instead


© Alex Ufkes, 2020, 2021 98
strcpy

Found in string.h

char city1[8] = ;
char city2[8];
strcpy(city2, city1)

Copies string city1 into string city2.

© Alex Ufkes, 2020, 2021 99


String Copy

char city[8];
city = ;

A string can be initialized ONLY

Use strcpy instead


© Alex Ufkes, 2020, 2021 100
strcpy

Found in string.h

char city[8];
strcpy(city, );

Copies string literal into city.


strcpy copies right string into left string
© Alex Ufkes, 2020, 2021 101
Summary

Memory Management in C:
Static arrays, strings
Pointers, addresses
Dynamic allocation
Memory (un)safety in C

© Alex Ufkes, 2022 102


© Alex Ufkes, 2022 103

You might also like