#include #include //Example for the 'Function pointers' section. static char *(*get_strcpy_ptr(void))(char *dst, const char *src); //Examples for the 'Strings' section. static size_t strlen1(const char *str); static size_t strlen2(const char *str); int main(void) { //I use these blocks ({}) for scoping. In particular, there are two 'foo_ptr' variables here, of different types. They must be in separate scopes. { //---------------------------------------------------------------------- /*Starting off*/ int foo = 3; int *foo_ptr = &foo; //foo_ptr is a pointer to foo; its type is int * (pointer to int) printf("Value of foo: %p\n", foo); printf("Address of foo: %p\n", &foo); printf("Value of foo_ptr (= &foo): %p\n", foo_ptr); puts("---"); //---------------------------------------------------------------------- /*Declaration syntax*/ //This way is evil: int* ptr_a, ptr_b; //Type of ptr_b is int - NOT int *! //Only ptr_a is actually a pointer. //Better way: int *ptr_c, ptr_d; //ptr_d is more clearly not a pointer int ptr_e, *ptr_f; //Even clearer here //Ultimate clarity int ptr_g; int *ptr_h; //Gratuitous parentheses are allowed around the asterisk and variable name in a declaration. int ((not_a_pointer)), (*ptr_i), (((*ptr_j))); //---------------------------------------------------------------------- /*Assignment and pointers*/ //This line elicits a warning in gcc: foo_ptr = 42; //Remember that foo_ptr is (was) a pointer to foo. //You might have expected that this line would set foo to 42 through the pointer, but it doesn't. //Instead, we changed the pointer that's in foo_ptr; foo is unchanged. What changed is that foo_ptr no longer points to foo; it points into random wilderness. printf("foo (after foo_ptr = 42): %i (note that foo is unchanged)\n", foo); //Prints 3 (see?) //---------------------------------------------------------------------- /*Dereferencing*/ foo_ptr = &foo; *foo_ptr = 42; //This is what we meant to do (note dereference operator, prefix *) printf("foo (after *foo_ptr = 42): %i\n", foo); //Prints 42 int bar = *foo_ptr; //Dereference as read; bar now == 42 puts("---"); //---------------------------------------------------------------------- /*Interlude: Arrays*/ //Here's an array: int array[] = { 45, 67, 89 }; //... that we can NEVER REFER TO AGAIN. //Here's how we try: printf("array: %p\n", array); //Prints some hexadecimal string like 0x12307734 //But %p means 'pointer'. We used a pointer to the array's first element (&array[0]) here. The array, you see, decayed to a pointer. int *array_ptr = array; //Equivalently: array_ptr = &array; //This & is implicit in the first method array_ptr = &array[0]; //---------------------------------------------------------------------- /*Pointer arithmetic (or: why 1 == 4)*/ printf(" first element: %i\n", *(array_ptr++)); printf("second element: %i\n", *(array_ptr++)); printf(" third element: %i\n", *array_ptr); /*output: * first element: 45 *second element: 67 * third element: 89 */ //Each ++ expression adds 1 to the variable. But in fact, the pointer moves more than 1 byte, because it is an int pointer (it moves sizeof(int) bytes). The amount added (1) is multiplied by the size (sizeof(int)) of the type of the pointer (int). //If it was a void pointer (void *), the multiplier would be 1. putchar('\n'); //blank line //---------------------------------------------------------------------- /*Indexing*/ printf("array[0]: %i (first element)\n", array[0]); //Prints 45 //Believe it or not, the index operator has nothing to do with arrays. //Remembering that the type of the pointer is int, array[0] == array + (0 * sizeof(int)). //A better example: /*int array[] = { 45, 67, 89 }; *int *array_ptr; */ array_ptr = &array[1]; puts("array_ptr = &array[1]"); printf("array_ptr[1]: %i (last element)\n", array_ptr[1]); //Prints 89 /*Stated diagrammatically: *array indices: 0 1 2 * ^ * | * array_ptr * | * v *array_ptr indices: 0 1 */ puts("---"); } { //---------------------------------------------------------------------- /*Interlude: Structures and unions*/ struct foo { size_t size; char name[64]; int answer_to_ultimate_question; unsigned shoe_size; } my_foo; //Direct structure access. my_foo.size = sizeof(struct foo); struct foo *foo_ptr = &my_foo; printf("Address of my_foo: %p\n", &my_foo); printf("Value of foo_ptr (= &my_foo): %p\n", foo_ptr); putchar('\n'); //blank line //This is the way we already know about: (*foo_ptr).size = sizeof(struct foo); //But there's a better way: foo_ptr->size = sizeof(struct foo); //Same thing, but shorter. //More levels of indirection make it uglier: struct foo **foo_ptr_ptr = &foo_ptr; (**foo_ptr_ptr).size = sizeof(struct foo); (*foo_ptr_ptr)->size = sizeof(struct foo); //The Pascal way (sigh): //foo_ptr_ptr^^.size := new_size; printf("my_foo.size: %zu\n", my_foo.size); putchar('\n'); //blank line printf("(*foo_ptr).size: %zu\n", (*foo_ptr).size); printf("foo_ptr->size: %zu\n", foo_ptr->size); putchar('\n'); //blank line printf("Address of foo_ptr: %p\n", &foo_ptr); printf("Value of foo_ptr_ptr (= &foo_ptr): %p\n", foo_ptr_ptr); putchar('\n'); //blank line printf("(**foo_ptr_ptr).size: %zu\n", (**foo_ptr_ptr).size); printf("(*foo_ptr_ptr)->size: %zu\n", (*foo_ptr_ptr)->size); puts("---"); } //-------------------------------------------------------------------------- /*Multiple indirection*/ int a = 3; int *b = &a; int **c = &b; int ***d = &c; *d == c; //Dereferencing an (int ***) once gets you an (int **) (3 - 1 = 2) //NOTE: This line causes a warning (claiming comparison between pointer and integer) in gcc 4.0.0 (Xcode 2.1). Ignore it; the expression IS correct. All three elements in the following expression are pointers. **d == *c == b; //Dereferencing an (int ***) twice, or an (int **) once, gets you an (int *) (3 - 2 = 1; 2 - 1 = 1) ***d == **c == *b == a == 3; //Dereferencing an (int ***) thrice, or an (int **) twice, or an (int *) once, gets you an int (3 - 3 = 0; 2 - 2 = 0; 1 - 1 = 0) //Thus, the & operator can be thought of as adding asterisks (or 'increasing pointer level'), and the * and [] operators as removing asterisks ('decreasing pointer level'). printf("Address of a: %p\n", &a); printf("Value of b (= &a): %p\n", b); putchar('\n'); //blank line printf("Address of b: %p\n", &b); printf("Value of c (= &b): %p\n", c); printf("Value of *c (= &a): %p\n", *c); putchar('\n'); //blank line printf("Address of c: %p\n", &c); printf("Value of d (= &c): %p\n", d); printf("Value of *d (= &b): %p\n", *d); printf("Value of **d (= &a): %p\n", **d); putchar('\n'); //blank line printf("Value of a = *b = **c = ***d: %i\n", a); puts("---"); //-------------------------------------------------------------------------- /*Pointers and const*/ //Two statements that are not equivalent: const int *ptr_a; //Here, the int (*ptr_a) is const. '*ptr_a = 42' is illegal. int const *ptr_b; //Here, the pointer (ptr_b) is const. '*ptr_b = 42' is legal, but '++ptr_b' is not. //-------------------------------------------------------------------------- /*Function pointers*/ { enum { str_length = 18U }; //Remember the NUL terminator! char src[str_length] = "This is a string.", dst[str_length]; //Functions decay to pointers, the same as arrays do. //When you call a function, you really call an address. strcpy(dst, src); //The function call operator in action. Notice the function pointer on the left side. //Function pointers are declared similarly to variables, but with parentheses in order to separate the * indicating a pointer from the zero or more * in the return type. //char *strcpy(char *dst, const char *src); //An ordinary function declaration, for reference char *(*strcpy_ptr)(char *dst, const char *src); //Pointer (indicated by inner *) to strcpy-like function strcpy_ptr = strcpy; //Decay: implicit & strcpy_ptr = &strcpy; //This works the same //strcpy_ptr = &strcpy[0]; //But not this //Even with the parameter names removed, this is still the same type as strcpy_ptr. char *(*strcpy_ptr_noparams)(char *, const char *) = strcpy_ptr; //Type of strcpy_ptr: char *(*)(char *dst, const char *src) //You use this in a cast: void *my_strcpy = strcpy; //Imagine that you got this somewhere else (e.g. that my_strcpy was a function parameter) strcpy_ptr = (char *(*)(char *dst, const char *src))my_strcpy; //As you might expect, a pointer to a pointer to a function has two asterisks inside of the parentheses: char *(**strcpy_ptr_ptr)(char *, const char *) = &strcpy_ptr; //And we can have an array of function-pointers: char *(*strcpies[3])(char *, const char *) = { strcpy, strcpy, strcpy }; char *(*strcpies2[])(char *, const char *) = { strcpy, strcpy, strcpy }; Array size is optional, same as ever strcpies[0](dst, src); //Here's a pathological declaration, taken from the C99 standard. /*'[This declaration] declares a function f with no parameters returning an int, * a function fip with no parameter specification returning a pointer to an int, * and a pointer pfi to a function with no parameter specification returning an int.' *(6.7.5.3[16]) */ int f(void), *fip(), (*pfi)(); //Equivalent to: int f(void); int *fip(); //Function returning int pointer int (*pfi)(); //Pointer to function returning int //A function pointer can even be the return value of a function. //char *(*get_strcpy_ptr(void))(char *dst, const char *src); strcpy_ptr = get_strcpy_ptr(); //Because function pointer syntax is so mind-bending, most developers use typedefs to abstract them: typedef char *(*strcpy_funcptr)(char *, const char *); strcpy_funcptr strcpy_ptr2 = strcpy; //strcpy_funcptr get_strcpy_ptr(void); } //-------------------------------------------------------------------------- /*Strings (and why there is no such thing)*/ char str[] = "I am the Walrus"; /*This array (str) is 16 bytes (16 chars) long. *The 'string' is 15 characters. *The last element of the array (str[15]) is 0, called 'NUL' in ASCII. *Thus it is called the 'NUL terminator', because it signals the end of the string. *But, aside from string literal syntax ("foo"), this is just an idiom. *The idiom is also supported by C's 'string library' (the contents of string.h). */ size_t len = strlen(str); //len = 15 printf("str: %s\n", str); printf("Your library's strlen: %zu\n", len); printf("Demonstration strlen (strlen1) using pointer arithmetic: %zu\n", strlen1(str)); printf("Demonstration strlen (strlen2) using indexing: %zu\n", strlen2(str)); return 0; } static char *(*get_strcpy_ptr(void))(char *dst, const char *src) { return strcpy; } static size_t strlen1(const char *str) { //Note the pointer syntax here //Remember, there is no 'string' here. There is only an array of characters, of which the last one is 0. size_t len = 0U; while(*(str++)) ++len; //When *str is 0, the loop exits. return len; } static size_t strlen2(const char *str) { size_t i; for(i = 0U; str[i]; ++i); //When the loop exits, i is the length of the string. return i; }