CS/CE 218 Lecture -*- Outline -*- connection: so far we've examined pointers only to primitive types. But the most useful kind of pointer is one to an element of an array. * Pointers and Arrays (section 5.3) advert: One thing that makes C so efficient is that there is a close connection between pointers and arrays; they're two sides of the same coin in C. Computing the address of successive elements in an array is more expensive than incrementing a pointer, but many find array indexing a more natural way to think about pointer arithmetic; and so in C you can use either style, and you can mix them. ** picturing arrays and array names In C an array name does not name an object, it's simply an address int a[7]; a++; illegal! &a illegal! but the indexes do name objects ++a[3], &a[3] ------------------------------------------------------------- int a[7]; int * ptr = a; a --\ ptr:|----| \ | &a[2] | ptr+5 | --|--\ | | | |----| \ v v v \--->|----------------------------------- | | | | | | | | |----------------------------------- a[0] a[1] a[2] a[3] a[4] a[5] a[6] ------------------------------------------------------------- unlike the book, a and ptr: are not the same a is just a constant (the address of a[0]) ptr is the name of an object (it contains the address of a[0]) ** pointer arithmetic *** pointer arithmetic and array indexing (pages 98-99) a[i] is equivalent to *(a+i) ptr[i] is equivalent to *(ptr+i) these equivalences work the other way around too so can use array notation with pointers and vice versa Ways to intialize the array a... int a[7] = { 0, 1, 2, 3, 4, 5, 6 }; ------------------ int i; for (i=0; i < 7; ++i) { a[i] = i; } int i; for (i=0; i < 7; ++i) { *(a+i) = i; } int i; int *ptr; for (ptr=a, i=0; i < 7; ++ptr, ++i) { *ptr = i; } int *ptr; for (ptr=a; ptr < &a[7]; ++ptr) { *ptr = (int) (ptr - a); /* diff of ptrs is type ptrdiff_t */ } ------------------ ptr = a is equivalent to ptr = &a[0] ptr = a+1 is equivalent to ptr = &a[1] (i.e., ptr = &(*(a+1))) can add and subtract integers, can subtract pointers ptr1-ptr2, but not ptr1+ptr2 can subtract and compare pointers within a given array ptr1 - ptr2 only defined if both point into same array *** pointer arithmetic and element size (section 5.4) Consider a machine where pointers are to particular bytes (chars) short = 2 bytes, long = 4 bytes, double held in 8 bytes ----------------------- char ca[10]; /* 10 bytes */ short sha[10]; /* 20 bytes */ double lots[10]; /* 80 bytes */ double *p, *q; p = lots; q = p + 1; printf("%d\n", q - p); /* prints 1 */ printf("%d\n", (int) q - (int) p); /* prints 8 */ ------------------------ suppose lots stored at address 500. then store 500 in p for p = lots; store 508 in p for q = p + 1 (why not 501?) q has to address the next element of lots the difference q - p is 1, because one element of array diff. recall (int) q converts q from type (double *) to int, doesn't change the value of the double pointed to, gives the value of the pointer as an integer (508) ** arrays as arguments (pages 99-100) Q: If all arguments in C are passed by value, how is it that a function can change the elements of the array? recall that an array name just denotes an address (not an object) this is a value, so in f(my_array) what is passed is the value of my_array, the address &my_array[0] so effectively, arrays are passed by reference! ** array types and pointer types *** In definitions ---------------- int a[3]; int *ptr; ptr = a; ptr++; /* legal, ptr is an object */ a++; /* illegal, a is not an object */ ---------------- an array declaration allocates space for elements, the name itself (a) is not allocated as a var a pointer declaration always allocates space for the pointer as a variable. DRAW THE PICTURE For allocating arrays, need to give the size if not initialized ---------------- int b[]; /* illegal, no size given */ int c[] = {1, 3, 5, 7}; /* ok, initialized */ int c2[4] = {1, 3, 5, 7}; /* ok, same as above */ ---------------- *** As function arguments as the declaration of a parameter (formal argument) ONLY: double [] is equivalent to double * Q: Can the following two functions be used in the same way? --------------- void initialize(double x[], int size) { for (--size; size >= 0; --size) x[size] = 0.0; } void initialize2(double *x, int size) { for (--size; size >= 0; --size) *(x++) = 0.0; /* you can leave the parentheses out */ } --------------- The types of these two funtions are considered to be the same! that is double [] is equivalent to double * for type checking of parameters to functions. ** subarrays (page 100) Q: How can you pass all but the first 2 elements of my_array to the initialize function? initialize(&my_array[2]); Q: Can you use negative numbers as subscripts on an array? yes! if you know that there are elements there... ** character pointers and strings (section 5.5) One of the most common types of arrays in C is character strings here are some things to watch out for... A string constant "hi" is an array of (constant) characters. it may not be modified --------------- char *cs; cs = "CS"; cs[1] = 'E'; /* undefined, gives error with gcc */ --------------- recall that strings are terminated by '\0' character draw picture for above and for char dept[] = "Comp Eng" and draw dept as an address, not labeling the array (as in book) *** strcpy example (page 105) -------------- void strcpy(char *s, char *t) /* requires: s has as much space as t has elements */ /* effect: copy t to s */ { int i; for (i = 0; (s[i] = t[i]) != '\0'; i++) ; } -------------- I used a for instead of a while... it's okay to use array notation for pointer arguments here's the pointer version that translates the above first we write s[i] as *(s+i), and then eliminate i from the program by incrementing s and t instead can change s and t because they are passed by value (doesn't change the characters) -------------- void strcpy(char *s, char *t) /* requires: s has as much space as t has elements */ /* effect: copy t to s */ { for (; (*s = *t) != '\0'; s++, t++) ; } -------------- then can optimize even more by using the postincrement ++ recall that *t++ gets *t, and then increments t then use equivalence of while and for, then recall that '\0' is just 0, and test is against 0 anyway... then declare s and t as register variables -------------- void strcpy(register char *s, register char *t) /* requires: s has as much space as t has elements */ /* effect: copy t to s */ { while (*s++ = *t++) ; } -------------- the version in the standard library returns the original value of t what would we do to change ours to do that? Q: Is this last version faster than the first? yes, but depends code size is a little smaller for pointer versions too... with gcc -O (on a sparc 2) 20 million calls with a 27 char arg version time (sec) 1 11.5 2 11.0 3 11.5 (about 4.5 sec of these times are overhead of measurement including function call time) with gcc, NO optimization 1 25.6 2 19.8 3 16.3 (about 11.8 sec of these times are overhead of measurement) Q: What would you write to push or pop a stack? for push, want to put value on top (*p), then increment *p++ = val (see page 106) Q: Can you do exercise 5-3 on page 107?