You are on page 1of 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

Address

i 6 1084 int i = 6; p 1084 1088 int *p; 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; Memory int j; i 4 int *p; j 6 p = &i; p 1084 j = *p; printf("%d %d\n", i, j); *p = 4; printf("%d %d\n", i, j);
Address 1084 1088 1092

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... for now
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: Memory Address
int i = 4; int j = 6; int *p = &i; int *q = &j; int **r = &p; printf("%d\n", **r); *r = &j; printf("%d\n", *p);
i j p q r 4 6 1088 1088 1092 1084 1088 1092 1096 1100

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++; /* ERROR */ j++; /* ERROR */

Order of type specifier and const modifier does matter when dealing with pointers:
int i = 4, j = 5; const int *p = &i; int * const q = &j; p = &j; /* OK */ *p += 5; /* ERROR q = &i; /* ERROR *q += 23; /* OK */ /* pointer to constant int */ /* constant pointer to int */ */ */

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++; } return sum; }

11 22 5 -1 2394 45346

1148 1152 1156 1160 1164 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); } h e l l o
str

\0 ptr

33

Pointer arithmetic, cont.


By adding an integer n to a pointer, we can get the address of the nth 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[] = { arg_vector "prog", 1056 prog\0....... 1056 "1", 1192 1\0.......... 1192 "two", 1324 THREE\0...... 1584 "THREE", 1584 two\0........ 1324 "-4", 1776 -4\0......... 1776 0 NULL }; argv looks just like this, including the NULL at the end
17

More examples of pointers


Program:
#include <stdio.h> 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; }

Output:
13 3 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

Pointer aliases
We can have two pointers pointing to the same address Remember freeing the space doesnt 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

!%

!"#"$$%

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

*%

You might also like