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 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 = &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:
Memory
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;

{

}

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 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 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

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 /* not #including 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 *%