1 software project: rehearsal. 2 overview rehearsal on selected topics in c memory organization:...

63
1 Software Project: Rehearsal

Post on 20-Dec-2015

217 views

Category:

Documents


3 download

TRANSCRIPT

1

Software Project:Rehearsal

2

Overview Rehearsal on selected topics in C

Memory organization: Stack and heap Arrays, pointers, and strings Structures Binary tree example (including function

pointers) Macros (preprocessor)

Sample exam questions Additional examples and topics

3

Memory organization

4

Storage organization

Attempting to access an address outside the allocated partition (segment) of the program - a segmentation error (access violation) and core dump (only unix/linux).

The program stack grows for each function call

5

Runtime Stack Activation records

(0) - inactive

(1) - inactive

(2) - active

6

Arrays, Pointers and Strings

a b c d e \0p

7

Pointers and pointed values (1)

#include <stdio.h>

int main(){ int a = 10, b = 2; int *p1, *p2; p1 = &a; p2 = &b; printf("%d %d \n", *p1, *p2); printf("%p %p \n", p1, p2); return 0;}

0012FF7C

a: 100012FF78

b: 2

p1: p2:

0012FF7C 0012FF78

type = pointer.Prints a value of a pointer as an hexadecimal integer

8

Pointers

pointer = address in memory Pointer definition: TYPE *p

TYPE: primitive types (char, int,...), pointers, structs,...

* operator – returns the pointed memory (the type of *p is TYPE) *p == p[0]

& operator = returns the address (pointer). p == &(*p)

9

Pointers and pointed values (2)

#include <stdio.h>

int main(void){

int i = 42, j=17, *ptr;

ptr = &i;

*ptr = 1;

ptr = &j;

*ptr = 2;

printf ("The value of i is %d\n", i);

printf ("The value of j is %d\n", j);

return 0;

}

0012FF7C

i: 420012FF78

j: 17

ptr:

0012FF7C0012FF78

1 2

$%@#%

10

Pointer initialization

void f1(){ char *p; *p = ‘a’; .....}

p is not initialized p contains “garbage”p may point to any memory address (legal / illegal)

void f2(int *a){ char *p; p = (char*)a; .....}

void f4(){ char *p; p = (char *)malloc(...); .....}

void f3(){ char c, *p; p = &c; .....}

X

assignment between any two pointers (using cast)

Dynamic allocation - on heap - no automatic de-allocation

11

realloc

accepts an old pointer and a new size Attempts to allocate memory in the original position.

If the size of the old block can be changed - the same pointer is returned.

otherwise, it Attempts to allocate new memory. If successful:

Copies old data to new memory Frees old memory block Returns new pointer

Otherwise, returns NULL (no free of ip)

int ip = malloc(10*sizeof(int);...... ip = realloc(ip, 200 * sizeof(int));

12

Verifying allocated memory malloc may fail to allocate memory - returns a null

pointer upon failure. It is vital to check the returned pointer before

using it! (even if the requested memory size is small)

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

if (ip == NULL) { printf("ERROR: out of memory\n");

return -1;

}

13

Memory leaks

Memory leak = Non-reachable, non-freed allocated memory.

In java / c#: the garbage collector frees “memory leaks”

void f(){ int *p = malloc(10*sizeof(int));}

The memory pointed by p remains allocated. However there is no way to access it.

14

(Heap) Memory de-allocation

Memory free == memory recycling Avoiding memory leaks:

Keep track of the allocated memory For every malloc there should be a corresponding free

The one who allocates is the one who frees (memory, files, etc.)

free - a non-expensive function (allocating memory is more expensive)

Any allocated memory is automatically released upon exit by the operating system. However, it is a coding standard to free the memory (and other resources) that you don’t need as soon as possible.

15

Accessing de-allocated memory (1)

De-allocated memory should not be accessed! De-allocated heap-memory is immediately usable

for next memory allocation. After calling free(p); p still points to the same

memory address Do not use the memory pointed to by p after

free(p)!

while (ptr != NULL) { free(ptr); ptr = ptr->next;

}

Illegal reference to deallocated memory.Unknown results...

16

Pointers arithmetic

Q: If a pointer p contain the value x (address), what is the value of p+1?

A: depends on the type of p (TYPE *) => x+sizeof(TYPE)

p[n] == *(p+n)

17

Example: printing the bytes of an integer

void print_bytes(int n){int i;char *pc = (char *)&n;

for (i=0; i < sizeof(int); i++, pc++){printf(“%d “,*pc);

}printf(“\n”);

}

char char char charint

Use sizeof(int) for portability.

18

Arrays

void f1(){ TYPE a[10]; int b1[] = {1,2}, b2[10] = {1,2}; char c1[] = {‘a’,’b’,’c’}, c2[] = “abc”; printf(“%d %d %d %d %d\n”, sizeof(a), sizeof(b1), sizeof(b2), sizeof(c1), sizeof(c2));}

Automatic allocation – on stack De-allocated automatically Overall size is given by sizeof()

Q: Assume sizeof(TYPE) = 5, sizeof(int)=4, sizeof(char)=1 What is the output of f1()?

A: 50 8 40 3 4

sizeof() is an operator that computes the size in bytes. It is evaluated at compilation time.

constant expression (compilation time)

19

Pointers vs. Arrays Similar arithmetic:

int a[10] *(a+2) == a[2] (no check of index boundaries, e.g. a[-1]) Pointers can point to elements in an array

Example: int a[10]; int *p = a; // p points to a[0] Pointers and arrays are not the same:

Pointer Array

Allocation malloc etc. – on heap. Any size.

On definition (automatic) - on stack. Size must be known at compilation

De-allocation Explicit command (free) Automatic

Constant inite.g. a = {1,2,3}

Disallowed Allowed - on declaration

Pointer / array Assignment (a = b)

Allowed Disallowed

sizeof Fixed pointer size (no relation to pointed memory)

Overall array size

20

Accessing de-allocated memory (2)

Local variables (stack memory) are de-allocatedautomatically on the return of their function

char *wrong_allocate(){ char s[] = "123"; return s;{char *valid_allocate(){ char* s1 = "456"; char* s = malloc(strlen(s1)+1); strcpy(s,s1); return s; }int main(int argc, char ** argv){ printf("%s %s \n",wrong_allocate(), valid_allocate());{

s points to a memory on stack, which is freed when the function returns

21

Multidimensional Arrays

int a[3][4];int i, j;

&a[i][j] = a[i]+j = ((int *)a)+i*4+j

int a[3][4];int i, j;

&a[i][j] = a[i]+j = ((int *)a)+i*4+j

C has only one-dimensional arrays:

(i,j) (i,j)<=>

Dimensions are known at compilation.

22

Dynamic matrix allocation (1) Q: What if the matrix dimensions are

unknown at compilation? A: use pointers and dynamic allocation

The matrix can be ragged – i.e. rows can have different sizes.

int i;int **a = malloc(n*sizeof(int*));for (i=0; i <n; i++)

a[i] =malloc(m*sizeof(int));

int i;int **a = malloc(n*sizeof(int*));for (i=0; i <n; i++)

a[i] =malloc(m*sizeof(int));

a

23

Dynamic matrix allocation (2)

Allocating an nm matrix using 2 allocationsint **allocate_matrix(int n, int m){ int i; int **a = malloc(n * sizeof(int *)); int *p = malloc(n * m * sizeof(int)); /* get the space all at once */ for (i = 0; i < n; i++, p+=m) a[i] = p; return a;}

a

p

24

Strings

char *p = “abcde”;

a b c d e \0

char s[] = “abcde”;

a b c d e \0

s:

* Constant string “abcde” is kept somewhere1. Allocates a memory for pointer p2. Initializes p to the base address of “abcde”

Allocates and initializes 6 bytes for the arrays

p

25

Allocating memory for strings

All strings have a terminating '\0' character. Add 1 to the count that strlen returns when calling

malloc

char *somestring=“1234”, *copy;copy = (char *) malloc(strlen(somestring) + 1); /* +1 for \0 */ /* check malloc's return value */ strcpy(copy, somestring); int strlen(char s[]){

int i =0; while (s[i] != ‘\0’) ++i; return i;}

26

Structures

27

Things you can do with structures...

Define data types of structures Declare variables of structure type Initialize a structure variable (comma-separated list of

values enclosed in braces {}) Assign entire structures Pass as arguments to functions

(it is preferable to pass pointers to the structures) Define functions which return structures

(it is preferable to return pointers to the structures)

typedef struct _Node Node;

node1 = node2

Node node;

Node node = {0,NULL, NULL};

struct _Node { int value; struct _Node * right; struct _Node * left; };

28

Structs with Arraystypedef struct _Soldier {

int id; char name[64]; char *rank;} Soldier;

int main(){ Soldier s0, s1, s2 = {5705706, "Enzo Agada", "General"};

s0.id = 5705705; strcpy(s0.name, "John Doe"); /* incorrect: s0.name = "John Doe"; */ s0.rank = "Captain"; /* incorrect: strcpy(s0.rank, "Captain"); */ s1 = s0; printf("%d %s %s\n", s0.id, s0.name, s0.rank); printf("%d %s %s\n", s1.id, s1.name, s1.rank); printf("%d %s %s\n", s2.id, s2.name, s2.rank);}

5705705 John Doe Captain 5705705 John Doe Captain 5705706 Enzo Agada General

29

Example: Sorted binary tree

30

Data type declaration: Node

typedef struct _Node {

int value;

struct _Node * right;

struct _Node * left;

} Node;

node_ptr->value;

(*node_ptr).value;

equivalent

value

value value

Left Right

struct _Node { int value; struct _Node * right; struct _Node * left; };typedef struct _Node Node;

equivalent

31

Node allocation and initialization

Node *create_node(int value){ Node * tmp = (Node *) malloc(sizeof(Node)); if (!tmp) { fprintf(stderr, "ERROR: not enough memory!\n"); return NULL; } tmp->value = value;

tmp->left = tmp->right = NULL;

return tmp;}

Returns a new node1. Allocate memory for the new node.

3. Initialize node’s fields

2.Verify allocation

32

Inserting new values to an existing treeNode * insert(Node * root, int value){

Node * parent, *child, *new_node; new_node = create_node(value); if (!new_node) return NULL; if (!root) return new_node; child = root; do{ parent = child; if (child->value > value) child = child->left; else child = child->right; } while (child); if (parent->value > value) parent->left = new_node; else parent->right = new_node; return root;}

2

3

5

7

9

4

parent

4

new_node root

Create the new node

33

Traversing a binary tree (inorder)

void print_tree(Node *root)

{

if (!root) return;

print_tree(root->left);

printf("%d ", root->value);

print_tree(root->right);

}Printing the right subtree (recursive call)

Print the value of the current node.

Printing the right subtree (recursive call)

L R

P

Null Null Null Null

empty tree – do nothing

34

Example: pointer to a function

Suppose we want to perform operations on the tree nodes – using our tree traversal function, E.g. printing the nodes

Pass a traversal function a pointer the desired function

void print_node(Node * node){ printf("%d ", node->value);}

prints the value of a node

traverse_tree(root, print_node);

35

Example: pointer to a function (2) Define the type of the functions that the traversal

function can get

typedef void(Op)(Node *);parameters type

Return value Data type name, which can be used to define variables / parameters

void traverse_tree(Node * root, Op * op){ if (!root) return; traverse_tree(root->left, op); op(root); traverse_tree(root->right, op);}void print_tree(Node *root){

traverse_tree(root,print_node);}

Invocation of the function (passed as argument op)

36

Additional notetypedef struct node {

struct node * right; struct node * left; int value;

char *name;} Node;

void f(){Node *new_node;new_node = create_node(value);new_node->name = malloc(100);....free(new_node->name); free(new_node) ;

}

Every allocated field of a struct must be deallocated – before the struct itself is deallocated

37

Macros (preprocessor)gcc -E: runs only the preprocessor and writes to standard output. E.g.: gcc -E a.c > aa.c

38

Macros

Macros are not functions Even when written with all the parentheses

#define MAX(a,b) (((a)>(b)) ? (a) : (b))

int a[4];int biggest;

biggest = x[0];i = 1;while (i < 4) biggest = MAX(biggest, x[i++]);

#define MAX(a,b) (((a)>(b)) ? (a) : (b))

int a[4];int biggest;

biggest = x[0];i = 1;while (i < 4) biggest = MAX(biggest, x[i++]);

This code would have worked fine if MAX was a function

What is the problem with this

code?

39

Macros

Let’s expand the macro:

#define MAX(a,b) (((a)>(b)) ? (a) : (b))

int a[4];int biggest;

biggest = x[0];i = 1;while (i < 4) biggest = (biggest > x[i++]) ? biggest : x[i++];

#define MAX(a,b) (((a)>(b)) ? (a) : (b))

int a[4];int biggest;

biggest = x[0];i = 1;while (i < 4) biggest = (biggest > x[i++]) ? biggest : x[i++];

40

Macros

Macros are not statements Lets try to write the assert macro

assert(x > y); /* should print info only if x <= y */

#define assert(e) if (!(e)) assert_error(__FILE__,__LINE__)

assert(x > y); /* should print info only if x <= y */

#define assert(e) if (!(e)) assert_error(__FILE__,__LINE__)

if ((x > 0) && (y > 0)) assert(x > y);else assert(y > x);

if ((x > 0) && (y > 0)) assert(x > y);else assert(y > x);

This macro is not safe:What is the problem

with this macro?

41

Macros

assert(x > y); /* should print info only if x <= y */

#define assert(e) { if (!e) assert_error(__FILE__,__LINE__) }

assert(x > y); /* should print info only if x <= y */

#define assert(e) { if (!e) assert_error(__FILE__,__LINE__) }

if ((x > 0) && (y > 0)) { if (!(x > y)) assert_error(__FILE__,__LINE__) };else { if (!(y > x)) assert_error(__FILE__,__LINE__) };

if ((x > 0) && (y > 0)) { if (!(x > y)) assert_error(__FILE__,__LINE__) };else { if (!(y > x)) assert_error(__FILE__,__LINE__) };

This will solve the dangling else problem but will cause a problemwith the semicolon:

We can try and add braces:

The correct solution is:

#define assert(e) ((void)((e) || assert_error(__FILE__,__LINE__)))

#define assert(e) ((void)((e) || assert_error(__FILE__,__LINE__)))

What is the problem with this macro?

'else' without a previous 'if'

42

Sample Exam Questions

int main(void){ int a=1,b=2, c=3; what_happens(&a, &b, &c); printf("a=%d b=%d c=%d\n", a, b, c); return 0;

}void what_happens(int *a, int *b, int *c){

c = a; a = b; b = c;

printf("*a=%d *b=%d *c=%d\n", *a, *b, *c); return;}

a: b: c:

in what_happens:

0012FF7C

0012FF78 0012FF780012FF7C

0012FF78

0012FF8C0012FF7C

Printed 2 1 1

Question 1a: What is the output?

0012FF7C 0012FF78

b: 2

in main:

0012FF8C

c: 3a: 1

Printed 1 2 3

int main(void){ int a=5,b = 7; int* c = do_balagan(&a, &b); printf("In main:\na=%d b=%d *c=%d\n", a, b, *c); return 0;}

int* do_balagan(int* a, int* b){ int* c; c = b; b = a; a = c; *a = *a + 5; *b = *a + *b; printf("In balagan:\n*a=%d *b=%d *c=%d\n", *a, *b, *c); return c;}

0012FF7C

a: 50012FF78

b: 7

in main:

12

a: b: c:

in do_balagan:

0012FF7C

0012FF78 0012FF780012FF7C

0012FF78

17

Question 1b: What is the output?

45

real

image

1

2

c1:

real

image

3

5

c2:

1+2*2

abs2 = ((c2.image*c2.image)+(c2.real*c2.real)) =34

abs1 = ((c1.image*c1.image)

+(c1.real++*c1.real++))

=5

Reminder: the preprocessor performs textual substitutions before compilationQuestion 2: What is the output?

46

1st invocation:initial: a=2, count =0;final: count = 1*2 = 2;

2nd invocation:initial: a=2, count = 2;final: count =3*2 = 6;

Question 3: What is the output?

47

Pointer to the next entry

If it is the end of a word increment the count

If there is no space after the last word

Question 4: -Is the code valid? If no - why?-If yes - What does it do?(isspace returns 1 iff the given character is a white space)

What happens if strlen(s)=0? (i.e. s==`\0`)

48

Access to a freed pointer!!!

Question 5: -Is the code valid? If no - why?-If yes - What does it do?

49

Exam Grading (previous semester)

50

Good Luck in the exam!

(08.02.2008)

51

Additional examples and topics

52

getline() function - Description

Implement a function int getline(char line[], int max); Description: Reads from standard input into a given line array

till new-line, end-of-file, the given array is filled.If newline was reached – it is not copied into the given array.

line – the given array to be filled (output parameter) max – the size of the given array (i.e. maximal characters to

read) Return value = line length

0 – for empty line EOF – for end-of-file (and nothing was read)

53

getline() function - Usage

#include <stdio.h>#include <stdlib.h>

#define MAX_LINE 80

int main(){ char line[MAX_LINE]; getline(line, MAX_LINE); printf("%s\n", line); return 0;}

Define a fixed size array and pass it to the getline function

The getline function reads a line and we print it

54

int getline(char line[], int max){ int nch = 0, c; max = max - 1; while ( ( (c = getchar()) != EOF) && (c != '\n') && (nch<max) ){

line[nch] = c; nch++; } if (c == EOF && (nch == 0)) return EOF; line[nch] = '\0'; return nch;}

getline() function – Implementation

Reserve a place for ‘\0’

May happen when stdout is redirected to a text file.

55

getline() –Usage (dynamic allocation)int main(){

char * line; int linelen; printf("Specify the sentence length:\n"); scanf("%d", &linelen); if (linelen <= 0) { printf("ERROR: line length must be positive!\n"); return 1; } line = (char *) malloc(linelen); if (line == NULL) { printf("ERROR: out of memory!\n"); return 1; } flushall(); printf("\nPlease type your sentence:\n"); getline(line, linelen); printf("\nYou typed:\n%s\n",line);

return 0;}

You could have written:line = (char *) calloc (linelen, sizeof(char));

malloc() – allocates the required amount of bytes.

calloc() – allocates and initializes to zero bytes.

56

Searching for Substrings – Impl.

/* Finds out whether a given array appears as a sub array in another */

int * find_sub_array(int * array, int size, int * sub_array, int sub_size) {

int i, j; for (i = 0; i++ <= size - sub_size; ++array) { for (j = 0; j < sub_size; ++j){ if ( *(array + j) != sub_array[j]) break;

} if (j == sub_size) return array; } return NULL;}

Counter “i” is used to validate that we are still within the array boundaries. Note that i is incremented only after the condition i<=size-sub_size is evaluated

57

Searching for Substrings - Usage

#include <stdio.h>int main(){ int array1[] = {1, 45, 67, 1001, -19, 67, 89, 1004, -867, 34, 3, -1900,

10029, 34, 3, -1900}; int array2[] = {34, 3, -1900}; int * position; int offset = -1; position = find_sub_array(array1, sizeof (array1) / sizeof (int), array2, sizeof (array2) / sizeof(int)); if (position != NULL)

offset = position - array1 + 1; printf("array2 appears in array1 starting from place : %d.\n", offset); return 0;}

58

Dangling else

The meaning was two cases: x = 0 and x != 0 However else is associated with the closest unmatched if.

if (x == 0) if (y == 0) error();else { z = x + y; f(z);}

if (x == 0) if (y == 0) error();else { z = x + y; f(z);}

if (x == 0) if (y == 0) error(); else { z = x + y; f(z); }

if (x == 0) if (y == 0) error(); else { z = x + y; f(z); }

59

Dangling else

Always use curly braces ‘{}’ with if-else statements

if (x == 0) { if (y == 0) error();} else { z = x + y; f(z);}

if (x == 0) { if (y == 0) error();} else { z = x + y; f(z);}

60

Using CORE dumps

On Unix (including Linux) a process is usually killed if it access memory incorrectly This usually happens due to pointers going mad

and bad Before the process is killed important

information on why it was killed is saved in a core file This information can be used to pinpoint what

went wrong

61

Using CORE dumps

#include <stdlib.h>

int main() { int* i = NULL;

*i = 5; /* This will cause a core dump in Unix/Linux */

return 0;}

#include <stdlib.h>

int main() { int* i = NULL;

*i = 5; /* This will cause a core dump in Unix/Linux */

return 0;}

Lets look at this faulty program (test.c) that tries to access a NULL pointer

Running this program yields the expected error message:Segmentation fault (core dumped)

62

Using CORE dumps

To use the core dump the program must be compiled with debug information.

This is done by adding –g flag to the gcc compilation line For example: gcc –g test.c –o myTest

(Make sure that your core file was generated while running the program compiled with debug information before continuing to the next step - you may need to run the program again after adding flag)

gdb <program executable> coreorddd <program executable> core

gdb <program executable> coreorddd <program executable> core Graphical

Non graphical

63

Using CORE dumps

nova~> gdb myTest coreGNU gdb Red Hat Linux (5.2-2)Copyright 2002 Free Software Foundation, Inc.Core was generated by `./myTest'.Program terminated with signal 11, Segmentation fault.Reading symbols from /lib/i686/libc.so.6...done.Loaded symbols for /lib/i686/libc.so.6Reading symbols from /lib/ld-linux.so.2...done.Loaded symbols for /lib/ld-linux.so.2#0 0x080483d0 in main () at test.c:66 *i = 5;(gdb) where#0 0x080483d0 in main () at test.c:6#1 0x42017589 in __libc_start_main () from /lib/i686/libc.so.6(gdb)

nova~> gdb myTest coreGNU gdb Red Hat Linux (5.2-2)Copyright 2002 Free Software Foundation, Inc.Core was generated by `./myTest'.Program terminated with signal 11, Segmentation fault.Reading symbols from /lib/i686/libc.so.6...done.Loaded symbols for /lib/i686/libc.so.6Reading symbols from /lib/ld-linux.so.2...done.Loaded symbols for /lib/ld-linux.so.2#0 0x080483d0 in main () at test.c:66 *i = 5;(gdb) where#0 0x080483d0 in main () at test.c:6#1 0x42017589 in __libc_start_main () from /lib/i686/libc.so.6(gdb)

Your command line

The gdb analysis of the core