/* ** $Id: pointers.c 775 2008-02-16 01:28:29Z phf $ ** ** Some basic pointer examples from lecture. Note ** that there's *lots* more to say about pointers, ** please read the K&R book and one or two of the ** other resources/links on the course website. I ** did add lots of comments, so this is an edited ** and more informative version. :-) */ #include #include #include /* ** Several functions here, but start reading in ** main() below instead. */ /* A broken swap function. */ void swap_broken( int x, int y ) { /* ** Note that x and y are automatic *local* variables ** that will go away at the end of the function. The ** values we pass get *copied* into these variables, ** and if we make modifications to them, those will ** be visible only *within* the function. */ int t = x; x = y; y = t; /* ** At this point we indeed swapped x and y, but we ** did *not* swap anything as far as main() is ** concerned. */ } /* A working swap function. */ void swap_working( int *x, int *y ) { /* ** We're now passing two *pointers* to existing ** variables; as we saw earlier in main(), using ** pointers to access the actual requires the "*" ** operator. */ int t = *x; /* save value of variable pointed to by x */ *x = *y; /* move value from var pointed to by y into var pointed to by x */ *y = t; /* move saved value into variable pointed to by y */ /* ** This time we actually affected the variables ** defined in the main() function. Woohoo! :-) This ** is also known as "pass by reference" instead of ** "pass by value" which we saw before in the ** lecture on functions. */ } /* String copy operation in "nice" array style. Think "t = s" for strings. */ void scpy1( char t[], const char s[] ) { int i = 0; while (s[i] != '\0') { t[i] = s[i]; // copy the next character i = i + 1; // advance the index } t[i] = '\0'; } /* String copy using pointer-style instead. */ void scpy2( char *t, const char *s ) { while (*s != '\0') { *t = *s; // copy the next character t = t + 1; // advance target pointer s = s + 1; // advance source pointer } *t = '\0'; } /* String copy using terse pointer-style instead, variation 1. */ void scpy25( char *t, const char *s ) { while ( *s != '\0' ) { *t++ = *s++; // copy the next character *and* advance pointers } *t = '\0'; } /* String copy using terse pointer-style instead, variation 2. */ void scpy3( char *t, const char *s ) { while ((*t = *s) != '\0') { // copy the next character *and* check if done t++; s++; // advance the pointers } } /* String copy super-terse! Yep, this does the same thing... :-/ */ void scpy4( char *t, const char *s ) { while ( (*t++ = *s++) ); // no != '\0' necessary since that's 0 anyway :-/ } /* String length in array style, note temporary "len" variable. */ int slen1( const char s[] ) { int len = 0; while( s[len] != '\0' ) { len = len + 1; } return len; } /* String length in pointer style; look ma, no "len" anymore! */ int slen2( const char *s ) { const char *t = s; // remember where we started from while ( *(s++) ); // search '\0' but will stop one *after* the '\0' char return s - t - 1; // subtract pointers! then adjust by -1 // Note: In general, if you add a pointer and an integer, you get // another pointer back; if you subtract two pointers, you get an // integer that means the "distance" between the pointers. Adding // two pointers makes no sense though. :-) } /* ** The main() with pointer examples. :-) */ int main(void) { /* ** Just two variables, a and b. Imagine them as boxes ** the way I always draw them on the board. */ int a = 10, b = 20; /* ** If we write "a" in our code, the C compiler interprets ** that as "get the value out of the box called a". So we ** can only talk about the *value* in the box, not the box ** itself. ** ** If we want to do the latter, we need a new operator: ** writing "&" in front of a variable means "don't get the ** value out of the box, get me a 'pointer' to the box ** itself instead". You can think of a 'pointer' as a way ** to refer to a box without referring to its contents. ** ** We can even print that "pointer" thing (using %p is the ** right format, although we'll still get a warning). The ** meaning of the number that gets printed is not really ** important, what is important is that it doesn't change ** even if the content of "a" changes. And you can see that ** different variables have different numbers associated ** with themselves, so "pointers" are a unique way to refer ** to boxes instead of contents. */ printf( "a contains %d\n", a ); printf( "a itself is %p\n", &a ); a = 11; printf( "a contains %d\n", a ); printf( "a itself is %p\n", &a ); b = 11; printf( "b contains %d\n", b ); printf( "b itself is %p\n", &b ); puts( "--------------------" ); /* ** Note how integer variables allow us to store integer ** values. Obviously 'pointers' are values as well, and ** it would be nice to have a way to "store" them. And we ** can, but we need a new "type" to do it. Here we define ** "pi" to be a variable of type "pointer to integer" and ** then we assign the pointer to "a" to that variable "pi". ** Note that printing "pi" is the same as printing "&a", ** exactly like we would expect from an assignment. */ int *pi = &a; printf( "a itself is %p\n", &a ); printf( "pi contains %p\n", pi ); printf( "pi itself is %p\n", &pi ); puts( "--------------------" ); /* ** The last line should be interesting: Since "pi" is a ** box itself, although one holding as a value the name ** of another box, we can of course ask for a pointer to ** "pi" itself. So "&pi" is a pointer to a pointer if you ** want. Kinda neat. :-) ** ** But what if we had just "pi" and not the name "a"? ** Since "pi" denotes the box also known by the name "a" ** it should be possible to access that box through "pi". ** Indeed that is possible, using the new "*" operator: */ printf( "pi contains %p\n", pi ); printf( "*pi contains %d\n", *pi ); puts( "--------------------" ); /* ** Let's do this again: If we evaluate "pi" we get a ** pointer to the box called "a". But to get back at ** the *content* stored in "a" through "pi" we need ** to use the "*" operator. In a very real sense, "&" ** and "*" are "opposites" of each other: */ printf( "a contains %d\n", a ); printf( "*&a contains %d\n", *&a ); puts( "--------------------" ); /* ** Using pointers, we can use one and the same name ** to refer to different boxes over time. Here we ** assign a pointer to "b" to the same "pi": */ pi = &b; printf( "b itself is %p\n", &b ); printf( "pi contains %p\n", pi ); puts( "--------------------" ); /* ** We can also make assignments *through* pointers ** to change the box they refer to: */ printf( "b contains %d\n", b ); printf( "pi contains %p\n", pi ); *pi = 7; printf( "b contains %d\n", b ); printf( "pi contains %p\n", pi ); puts( "--------------------" ); /* ** But what is all of this really good for? We'll see ** more examples later, but for now there's one really ** good example. Say you want to write a function that ** exchanges two variables. So far we've only looked ** at simple value parameters, which generate copies ** of the values passed to a function. Using that ** mechanism, we cannot write such a function. Check ** out swap_broken() above and come back here. */ printf( "a contains %d\n", a ); printf( "b contains %d\n", b ); swap_broken( a, b ); printf( "a contains %d\n", a ); printf( "b contains %d\n", b ); puts( "--------------------" ); /* ** The reason for this failure is that inside the ** function, we don't refer to the boxes "a" and ** "b" from main, but to other boxes that got the ** values stored in "a" and "b" copied into them. ** If the function is to exchange the content of ** "a" and "b" then we have to pass the *boxes* ** instead of passing the values. That's what we ** can do with pointers. */ printf( "a contains %d\n", a ); printf( "b contains %d\n", b ); swap_working( &a, &b ); printf( "a contains %d\n", a ); printf( "b contains %d\n", b ); puts( "--------------------" ); /* ** That's a pretty good first application for ** pointers, isn't it? But pointers are not only ** used in the context of functions. Arrays, it ** turns out, are also closely related to pointers ** in C. Consider these two declarations: */ char bla[] = "Mary"; char *blu = "Peter"; puts( bla ); puts( blu ); puts( "--------------------" ); /* ** One is defined using array syntax, the other ** using pointer syntax. What's the difference? ** There really is none in C. That's the reason ** why we can path both to the same function ** without getting a warning about types! So ** what does "bla" or "blu" evaluate to? It ** turns out that they both evaluate to the ** char variable corresponding to the first ** character in each string. We can see that ** as follows: */ printf( "%c\n", bla[0] ); printf( "%c\n", *bla ); printf( "%c\n", *blu ); printf( "%c\n", blu[0] ); puts( "--------------------" ); /* ** As you can see, we can get the first character ** of either using either notation. In this case, ** array notation and pointer notation are 100% ** equivalent. It turns out that array notation ** is simply a nicer way to write a pointer+integer ** addition followed by the "*" operator. Consider ** the last example above. It should be obvious ** that the following does the same: */ printf( "%c\n", *(blu+0) ); /* ** Indexing with a value > 0 works the same way: */ printf( "%c\n", blu[3] ); printf( "%c\n", *(blu+3) ); /* ** And with some horror we have to admit that ** the following is also legal: */ printf( "%c\n", 3[blu] ); puts( "--------------------" ); /* ** The reason is that the C compiler simply replace ** "array notation" with "pointer+addition+dereference" ** internally. Since addition is commutative, the order ** between array and index doesn't even matter. Although ** it is extremely bad style to write that last example ** of course. :-) ** ** When we add "1" to a pointer, we say "go to the next ** element in this array" which of course means that +1 ** on a pointer may move the *actual* address further ** by more than 1: it moves the *size* of the element ** type of the array instead. Since for char arrays the ** size of an element is one byte, the change is ** identical. But for an integer pointer, incrementing ** it by 1 would move the actual address 2, 4, or 8 ** bytes, depending on how many bytes one integer takes ** up on the machine we're on. ** ** It is very common in C program to replace the traversal ** of an array using an index by a pointer traversal instead. ** The resulting programming style is sometimes considered ** "cryptic". Personally I like the clearer array style; but ** you need to be able to read the other style as well! Check ** out the following examples to see what I mean. They all ** implement the same kind of string-copy operation, but they ** do so *very* differently. */ char str[128]; strcpy( str, "First!" ); puts( str ); scpy1( str, "Second!" ); puts( str ); scpy2( str, "Third!" ); puts( str ); scpy3( str, "Fourth!" ); puts( str ); scpy4( str, "Fifth!" ); puts( str ); puts( "--------------------" ); /* ** I hope you're able to explain why scpy4 works like a ** charm as well? :-) But we can not only add an integer ** to a pointer, we can also subtract two pointers to get ** the distance between them (in elements). Consider the ** following functions to see how: */ printf( "%d\n", strlen( "Peter" ) ); printf( "%d\n", slen1( "Peter" ) ); printf( "%d\n", slen2( "Peter" ) ); puts( "--------------------" ); /* ** Okay, that's it. :-) */ return EXIT_SUCCESS; }