#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: %d\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 }; //Now, we try to refer to this array: 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]; //This equivalence doesn't hold for pointer variables, only array variables. See what happens when we try it with a pointer variable: int **array_ptr_ptr; array_ptr_ptr = array_ptr; //This will warn, because we are assigning a pointer of a different type: An int * value (array_ptr) to an int ** variable (array_ptr). array_ptr_ptr = array; //Same here: array decays to int *, but this variable holds int **. array_ptr_ptr = &array_ptr; //Valid: &array_ptr is a pointer to a variable holding a pointer to an int. array_ptr_ptr = &array_ptr[0]; //This will warn, because &array_ptr[0], like array_ptr itself, is an int *. //---------------------------------------------------------------------- /*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 subscript 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 putchar('\n'); //blank line //Stated diagrammatically: puts(" 0 1 2 <- indices into array"); puts(" 0 1 <- indices into array_ptr"); puts("-- -- --"); puts("45 67 89 <- values in the actual array"); 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: C does not support chaining comparisons (e.g., a == b == c); each relational operator only compares two operands at a time. So, for one example, (a == b == c) is actually ((a == b) == c). //In the examples below, this is a comparison of an integer (the result of the first comparison) to a pointer (the other term of the second comparison). This is not what I'd intended, and both GCC and Clang rightly warn about it. //I've maintained the chained-comparison style below for clarity, but commented the lines out to show that such expressions are to be disfavored. //Dereferencing an (int ***) twice, or an (int **) once, gets you an (int *) (3 - 2 = 1; 2 - 1 = 1): // **d == *c == b; //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): //***d == **c == *b == a == 3; //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*/ //The placement of the const keyword matters: const int *ptr_a; //Here, the int (*ptr_a) is const. '*ptr_a = 42' is illegal. int const *ptr_b; //Same as ptr_a. int *const ptr_c; //Here, the pointer (ptr_b) is const. '*ptr_c = 42' is legal, but '++ptr_c' is not. //Note the similarity of ptr_b and ptr_c. The first is equivalent to ptr_a; only the last is a different type where the pointer, not the int, is constant. Be sure which one you mean. //-------------------------------------------------------------------------- /*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; }

AltStyle によって変換されたページ (->オリジナル) /