Sunteți pe pagina 1din 25
 

!"!!"##$

Controlling formatting

We can supply a field width, precision, and other flags to format our output exactly as we want

%04x : format as unsigned hex number, with 4 spaces and zero padding – %-10s : format as string, allot 10 spaces, left justify (default is right justify) – %6.4f : format as floating point, allot 6 spaces, 4 digits after the decimal point

Much more detail available in §15.10.3

 

11

Chapter 6, Reek

POINTERS

 

12

 

($

!"!!"##$

Pointers

Pointers are variables whose value is an address • Every variable is stored at an address in memory • We use pointers to perform manipulation of memory, by accessing items at the address stored in the pointer

13

Declaration of pointers

A pointer to an int value would be declared like this: int *ip; Creates a variable called ip, whose type is "pointer to int" • We can assign the address of an int variable to be the value of this new pointer

14

)$

!"!!"##$

Pointer operators

Obtaining the address of an object (&)

 

Placed before a variable (or an object in memory)

Accessing the value at an address (*)

Placed before an expression which is either a pointer or otherwise evaluates to an address

•   Example: Memory int i = 6; int *p; i 6 p 1084
•   Example:
Memory
int i = 6;
int *p;
i
6
p
1084

Address

1084

1088

 

p

= &i;

printf("%d %d\n", *p, *(&i));

 

15

Using a dereferenced pointer

The * operator can be used on both the left and right sides of an assignment

int i = 6; int j; int *p; Memory i 4 j 6 p =
int i = 6;
int j;
int *p;
Memory
i
4
j
6
p
= &i;
p
1084

Address

1084

1088

1092

 

j

= *p;

printf("%d %d\n", i, j); *p = 4; printf("%d %d\n", i, j);

 

16

*$

!"!!"##$

Garbage pointers

When a pointer is declared, it points to whatever address was in the memory location allocated for the pointer (no initialization) • Trying to dereference this random address will generally result in one of three Bad Things:

accessing a memory location you don't have permission to access (a "segmentation fault") – violating the computer's alignment policies (a "bus

error") – silent failure: everything appears to work right now

for

17

NULL pointer

This is a pointer that points to the address 0, where nothing is allowed to be accessed • Defined in stddef.h, which is included by many other header files • Analogue to Java's null

What happens when you try to call a method of an object which is null? Very similar thing happens in C when trying to dereference a NULL pointer; it's usually a segfault

Just like in Java, you have to check pointers to see if they're NULL before dereferencing them:

void f(int *p) { if (p != NULL) *p = 55;

}

18

+$

!"!!"##$

Pointers to Pointers •   You can also obtain the address of a pointer variable:
Pointers to Pointers
•   You can also obtain the address of a pointer
variable:
Memory
Address
int i = 4;
int j = 6;
int *p = &i;
int *q = &j;
int **r = &p;
printf("%d\n", **r);
i
4
1084
j
6
1088
p
1088
1092
q
1088
1096
r
1092
1100
*r = &j;
printf("%d\n", *p);
•   This technique will be useful when working with
pointers as parameters
19

Pointers as parameters

You can also pass addresses into a function:

void swap(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp;

}

int x = 2; int y = 3; swap(&x, &y); printf("%d %d\n", x, y);

Why do we need to use pointers here?

20

#,$

!"!!"##$

Structures and pointers

We can also have pointers to structures:

typedef struct { int number, num_students, start_time; } Section;

void add_students(Section *sec, int students_to_add)

{

(*sec).num_students += students_to_add;

}

Section s = {101, 25, 1300}; add_to_students(&s, 5); printf("%d\n", s.num_students);

21

The -> operator

Dereferencing of a pointer to a structure must occur before accessing a field of the structure; due to precedence, parentheses are needed

Section s = {101, 25, 1300}; Section *sp = &s;

*sp.num_students += 5;

/* WRONG */

(*sp).num_students += 5; /* RIGHT */

C has a special operator to make this easier:

"(*sp).num_students" is equivalent to "sp->num_students"

22

##$

!"!!"##$

Don't forget to check for NULL

A common error is to do something like this: assume abs_val() is supposed to return the absolute value of the number pointed to by cp, or return -1 if cp is NULL

typedef struct { double real; double imag; } Complex;

double abs_val(Complex *cp) { double r = cp->real * cp->real; double i = cp->imag * cp->imag; if (cp == NULL) return -1; return r + i;

}

Remember that the -> is doing dereferencing; you must perform the NULL check before the pointer is dereferenced!

23

Generic pointers

Pointers to void (void *) can point to any type:

void *vp; int a, *ip; double b, *dp; vp = &a; ip = vp; vp = &b; dp = vp; vp = ip;

No casts needed with void * pointers

24

#!$

!"!!"##$

Generic pointers, cont.

You can't dereference a void * - you first need to cast or assign it to a real pointer type

the value obtained from a dereference depends on the type of pointer

NULL is really defined as (void *) 0 These allow use of generic code, but misuse can lead to the kinds of errors we've seen before:

void *vp; int *ip; double a = 3.14159; vp = &a; ip = vp; printf("%d\n", *ip); /* -266631570 */

25

Type conversion with pointers

Converting from one type to a pointer has some uses:

unsigned int i; unsigned char *ch; i = 0x543210ab; ch = (unsigned char *) &i; printf(“%d\n”, *ch); printf(“%d\n”, *(unsigned char *) &i);

Prints out either MSB or LSB of i, depending on architecture

26

#%$

 

!"!!"##$

 

Type conversion, cont.

 

Type conversion is very similar to what happens when we access an inactive union field

 
 

union { int i; double dbl; } a; double fp_val = 3.14159; a.dbl = 3.14159;

 

printf(“%d\n”, a.i); /* -266631570 */ printf(“%d\n”, * (int *) &fp_val);

27

 

Type conversion, cont.

 

The two lines are the same! • Although we stored a double-precision floating-point number at an address in memory, interpreting that address as a 4-byte integer results in a wildly different value from the floating-point number

 
 

10010101

01010110

10101010

11010100

00101010

01010100

10101010

10010011

 

28

 

#&$

 

!"!!"##$

 

The const modifier

 

Indicates that a variable can't be changed, and enforced by compiler

int const i = 4;

 

const int j = 5;

i++;

j++;

/* ERROR */ /* ERROR */

Order of type specifier and const modifier does matter when dealing with pointers:

int i = 4, j = 5; const int *p = &i;

/* pointer to constant int */

 

int * const q = &j; /* constant pointer to int */

p

= &j;

   

*p += 5;

/* OK */ /* ERROR */

q

= &i;

 

*q += 23;

/* ERROR */ /* OK */

The program cdecl (or http://cdecl.org) can be useful for decoding some more complex declarations

 

29

 

Incrementing pointers

 

Pointers can be incremented/decremented just like integer type variables, "moving" one element at a time

how much is added to the address depends on the size of the type to which the pointer points (as declared)

Recall arrays are contiguous memory • What does this function do?

 
   

int mystery(int array[]) { int *p = &(array[0]); int sum = 0; while (*p != -1) { sum += *p; p++;

11

1148

22

1152

5

1156

-1

1160

 
 

}

2394

1164

return sum;

45346

1168

}

30

 

#'$

 

!"!!"##$

Incrementing pointers, cont.

size_t strlen(const char *str) { size_t len = 0; while (*str) { len++; str++;

}

return len;

}

Why can we move the str parameter? • Why does this return the string's length?

 

31

Incrementing pointers, cont.

The postfix operators take precedence over the dereference operator and prefix operators • * and prefix ops are at the same precedence level, and associate right to left • ++*p increments the value at the location to which p points, and evaluates to the incremented value • *p++ evaluates to the value at the location to which p points, and then advances p (*p)++ evaluates to the value at the location to which p points, and then increments that value

 

32

 

#($

Pointer arithmetic

With two pointers in the same array, we can determine how far apart they are

size_t strlen(const char *str) { const char *ptr; for (ptr = str; *ptr; ptr++)

;

return (size_t) (ptr – str);

}

str

ptr

33

h

e

l

l

o

\0

!"!!"##$

Pointer arithmetic, cont.

By adding an integer n to a pointer, we can get the address of the n th element past the element to which the pointer currently points

int arr[] = {2, 3, 5, 7, 11}; int *p = &(arr[0]); int *q = p + 4; printf("%d\n", *q); Output: 11

Only valid forms of pointer arithmetic:

pointer - pointer – pointer ± integer

34

#)$

!"!!"##$

Pointer arithmetic, cont.

We can also use relational and equality operators when working with multiple pointers

void sum_subarray(int array[], int idx1, int idx2) { int *ptr; int sum = 0; ptr = array + idx1; while (ptr <= array + idx2) { sum += *ptr; ptr++;

}

return sum;

}

35

#*$

2/22/11

sizeof and arrays

The sizeof operator can be used to find the total size of an array, or the number of elements in an array

int arr[5]; int ct = (int) sizeof(arr) / sizeof(arr[0]); printf("%d\n", (int) sizeof(arr)); /* 20 */ printf("%d\n", ct); /* 5 */

But this doesn't work for array parameters:

void f(int arr[]) { printf("%d\n", (int) sizeof(arr)); /* 8 */

}

Why not? Remember what's passed to a function when we pass an array.

 

13

Arrays vs. Pointers

int nums[4];

declares an array, allocates 4 ints' worth of space, and points the name nums to the beginning of this space – nums cannot be changed to point elsewhere – by itself, nums is treated as a constant pointer that points to the beginning of the array

int *nump;

declares a pointer, doesn't allocate anything more than space to store an address, connects the name nump to that space – nump can be changed and assigned to

 

14

2/22/11

What subscripting really does

The expression array[num] is exactly equivalent to the pointer arithmetic expression *(array + num) The subscript operator ([]) can be used on both arrays and pointers, as can the dereferencing operator

int arr[5] = {10, 20, 30, 40, 50}; int *p = arr; printf("%d\n", p[3]); printf("%d\n", *arr);

 

15

Arrays of pointers

We can also have an array of pointers:

int *nums[60]; an array of 60 pointers to int

This is useful when dealing with arrays of pointers to structures

allows us to sort the pointers, without moving around tons of memory

This is also how the argv array is implemented

 

16

2/22/11

An array of pointers

 

char *arg_vector[] = {

 

"prog",

"1",

arg_vector

 
 

1056

1056

prog\0

 

1192

1192

1\0

"two",

1584

1324

THREE\0

"THREE",

1324

1584

two\0

"-4",

1776

1776

-4\0

NULL

0

 

}; argv looks just like this, including the NULL at the end

17

More examples of pointers

 

Program:

Output:

 

#include <stdio.h>

13

3

int main() { int arr[] = {2, 3, 5, 8, 13}; int *ptr = &arr[2]; printf("%d\n", ptr[2]); printf("%d\n", ptr[-1]); printf("%d\n", *(arr + 3)); printf("%d\n", ++*ptr); printf("%d\n", arr[2]); printf("%d\n", *ptr++); printf("%d\n", *ptr); printf("%d\n", arr[-1]); return 0;

}

 

8

6

6

6

8

?????

 

18

2/24/11

Chapter 11, Reek

DYNAMIC MEMORY ALLOCATION

CMSC 216 - Wood, Sussman, Herman

11

Stack vs. Heap Memory

Stack memory is allocated and deallocated in a specific order

you can only remove from and add to the top of the stack – any item in the stack is always freed before the item below it – useful for local variables, since functions work in similar manner

Heap memory

can be allocated and deallocated in any order while the program is running

CMSC 216 - Wood, Sussman, Herman

12

2/24/11

Dynamically allocated memory

Why use it?

to eliminate compile-time limits:

Often, the size of a data structure isn't known until runtime • Allocating a huge amount of space to fit every possible scenario can be error-prone at worst and a waste of space at best

User-managed heap vs. Garbage collection

garbage collection:

frees programmer from burden of keeping track of objects (convenience) • slows down execution due to collection needing to be run periodically; collection is also expensive

user-managed heap

programmer can control the precise time to deallocate objects • requires a lot more care - errors can occur if the wrong thing is freed, or the right thing is freed at the wrong time

CMSC 216 - Wood, Sussman, Herman

13

Memory management functions

Allocating memory:

void *malloc(size_t amount);

allocates amount bytes of memory (if available) from the heap and returns a pointer to the beginning of it • no initialization of the space

void *calloc(size_t count, size_t obj_size);

allocates count objects of size obj_size each (if memory is available), returns a pointer to beginning of it • initializes all the space to 0

both return NULL if the allocation fails – both require #include <stdlib.h> since prototypes are contained there

Note that both these functions return a void *

CMSC 216 - Wood, Sussman, Herman

14

2/24/11

Memory management functions, cont.

Deallocating memory

void free(void *ptr);

returns the memory pointed to by ptr to the free pool in the heap memory MUST have been previously allocated from the heap does not change ptr (why not?), so you may want to set ptr to NULL after calling free() if you might accidentally use ptr again free(NULL); does nothing - it's perfectly OK to do

CMSC 216 - Wood, Sussman, Herman

15

More about free()

Once you've freed memory, it goes back to the heap, meaning you can't trust its value to remain the same (or even stay valid!) • It's up to you to ensure you don't dereference a freed pointer - otherwise, weird things can happen

CMSC 216 - Wood, Sussman, Herman

16

 

!"#"$$%

Chapter 11, Reek

 

DYNAMIC MEMORY ALLOCATION (CONT.)

 

CMSC 216 - Wood, Sussman, Herman, Plane

3

 

Pointer aliases

We can have two pointers pointing to the same address • Remember freeing the space doesn’t change the value of the pointer or the value of the space pointed at

 

int *p, *q;

 

p

= malloc(sizeof(int));

 

*p = 99;

 

q

= p;

/*values of p and q and *p and *q*/

free(p);

p

= NULL;

*q = 42;

 

q is called a dangling pointer

 

CMSC 216 - Wood, Sussman, Herman, Plane

4

 

&%

!"#"$$%

Common errors

Dereferencing pointers to freed space (directly or indirectly) • Forgetting to check the return value from malloc() for NULL Not initializing the memory malloc() returns • Not allocating enough space:

char string[] = "Inconceivable!"; char *p = malloc(strlen(string));

strcpy(p, string); /* Oops

*/

CMSC 216 - Wood, Sussman, Herman, Plane

5

Common errors, cont.

Attempting to free non-heap memory:

int i, *p; p = &i; free(p); /* ??? Undefined operation */

Freeing something that's not the beginning of a dynamically allocated block:

int *p = calloc(10, sizeof(int)); free(p + 3);

CMSC 216 - Wood, Sussman, Herman, Plane

6

!%

 

!"#"$$%

Common errors, cont.

 

Dereferencing a random address (garbage pointer)

int *p; *p = 42;

Dereferencing a NULL pointer

 

int *p; p = NULL; *p = 23;

Dereferencing a dangling pointer

 

int *p = malloc( free(p);

);

*p = 27;

CMSC 216 - Wood, Sussman, Herman, Plane

7

Common errors, cont.

 

Referring to data past the end of allocated space

Same as statically allocated arrays

 

Using freed memory

List_node *ptr =

 

malloc

(sizeof(List_node));

free(ptr);

ptr = ptr->next;

/* Uh oh

*/

CMSC 216 - Wood, Sussman, Herman, Plane

8

 

'%

!"#"$$%

Common errors, cont.

What happens to the first Stack object in Java?

Stack s = new Stack();

s = new Stack();

Losing a pointer to dynamically allocated memory causes memory leaks

not an immediately fatal error – can just be a waste of resources, but left to run for a while, it can keep your program from being able to do anything

CMSC 216 - Wood, Sussman, Herman, Plane

9

Reallocation

If you need more space than you originally allocated, you could handle this yourself:

allocate array twice as large – copy everything over – free original array

But, there's a way to do this without nearly as much work on your part

CMSC 216 - Wood, Sussman, Herman, Plane

10

(%

 

!"#"$$%

Reallocation, cont.

void *realloc(void *p, size_t new_size);

checks current size of the block p points to

if original size >= new_size, can perform reallocation in place, and returns p if original size < new_size, new block of size new_size is allocated

if allocation fails, returns NULL otherwise, copies items from p over, free p, and returns pointer to new block

CMSC 216 - Wood, Sussman, Herman, Plane

11

Use of realloc()

Often, you'll see this:

ptr = realloc(ptr, size * 2);

But what happens if the allocation fails? • You can only do this if you know for certain you're going to exit the program on an allocation failure

CMSC 216 - Wood, Sussman, Herman, Plane

12

 

)%

 

!"#"$$%

Operating on blocks of memory

Much as we have functions like strcpy() that operate on strings of characters, we can copy whole blocks of memory (or strings of bytes, if you like)

void *memcpy(void *dst, void *src, size_t n); void *memmove(void *dst, void *src, size_t n);

Both copy n bytes from the memory starting at src to the memory starting at dst, and return the dst pointer • memcpy() cannot be used on overlapping arrays, though; but it's faster than memmove() Both prototypes contained in string.h

CMSC 216 - Wood, Sussman, Herman, Plane

13

A possible memcpy() implementation

#include <stdio.h> /* not #including <string.h> because of conflict */

void *memcpy(void *dst, void *src, size_t n) { char *dp = dst; char *sp = src; while (dp - (char *) dst < n) *dp++ = *sp++; return dst;

}

int main() { int i, j = 5; memcpy(&i, &j, sizeof(int)); printf("%d\n", i); return 0;

}

CMSC 216 - Wood, Sussman, Herman, Plane

14

 

*%