text: Pointers are arguably the most difficult feature of C to understand. But, they are one of the features which make C an excellent language.
  In this article, we will go from the very basics of pointers to their usage with arrays, functions, and structure.
  So relax, grab a coffee, and get ready to learn all about pointers.
  Topics
  A. Fundamentals
  B. Arrays and Strings
  C. Functions
  - Call by Value v/s Call by Reference
  - Pointers as Function Arguments
  - Pointers as Function Return
  - Pointer to Function
  - Array Of Pointers to Functions
  - Pointer to Function as an Argument
  D. Structure
  E. Pointer to Pointer
  F. Conclusion
  A. Definition, Notation, Types and Arithmetic
  1. What exactly are pointers?
  Before we get to the definition of pointers, let us understand what happens when we write the following code:
  int digit = 42;
  A block of memory is reserved by the compiler to hold an
  int value. The name of this block is
  digit and the value stored in this block is
  42.
  Now, to remember the block, it is assigned with an address or a location number (say, 24650).
  The value of the location number is not important for us, as it is a random value. But, we can access this address using the
  & (ampersand) or address of operator.
  printf("The address of digit = %d.",&digit); /* prints "The address of digit = 24650. */
  We can get the value of the variable
  digit from its address using another operator
  * (asterisk), called the indirection or dereferencing or value at address operator.
  printf("The value of digit = %d.", *(&digit); /* prints "The value of digit = 42. */
  2. Definition and Notation
  The address of a variable can be stored in another variable known as a pointer variable. The syntax for storing a variable's address to a pointer is:
  dataType *pointerVariableName = &variableName;
  For our
  digit variable, this can be written like this:
  int *addressOfDigit = &digit;
  or like this:
  int *addressOfDigit; addressOfDigit= &digit;
  This can be read as - A pointer to
  int (integer)
  addressOfDigit stores the
  address of(&)
  digit variable.
  Few points to understand:
  dataType – We need to tell the computer what the data type of the variable is whose address we are going to store. Here,
  int was the data type of
  digit.
  It does not mean that
  addressOfDigit will store a value of type
  int. An integer pointer (like
  addressOfDigit) can only store the address of variables of integer type.
  int variable1; int variable2; char variable3; int *addressOfVariables;
  * – A pointer variable is a special variable in the sense that it is used to store an address of another variable. To differentiate it from other variables that do not store an address, we use
  * as a symbol in the declaration.
  Here, we can assign the address of
  variable1 and
  variable2 to the integer pointer
  addressOfVariables but not to
  variable3 since it is of type
  char. We will need a character pointer variable to store its address.
  We can use our
  addressOfDigit pointer variable to print the address and the value of
  digit as below:
  printf("The address of digit = %d.", addressOfDigit); /* prints "The address of digit = 24650." */ printf("The value of digit = %d.", *addressOfDigit); /*prints "The value of digit = 42. */
  Here,
  *addressOfDigit can be read as the value at the address stored in
  addressOfDigit.
  Notice we used
  %d as the format identifier for
  addressOfDigit. Well, this is not completely correct. The correct identifier would be
  %p.
  Using
  %p, the address is displayed as a hexadecimal value. But the memory address can be displayed in integers as well as octal values. Still, since it is not an entirely correct way, a warning is shown.
  int num = 5; int *p = # printf("Address using %%p = %p",p); printf("Address using %%d = %d",p); printf("Address using %%o = %o",p);
  The output according to the compiler I'm using is the following:
  Address using %p = 000000000061FE00 Address using %d = 6422016 Address using %o = 30377000
  This is the warning shown when you use %d - " warning: format '%d' expects argument of type 'int', but argument 2 has type 'int *' ".
  3. Some Special Pointers
  The Wild Pointer
  char *alphabetAddress; /* uninitialised or wild pointer */ char alphabet = "a"; alphabetAddress = &alphabet; /* now, not a wild pointer */
  When we defined our character pointer
  alphabetAddress, we did not initialize it.
  Such pointers are known as wild pointers. They store a garbage value (that is, memory address) of a byte that we don't know is reserved or not (remember
  int digit = 42;, we reserved a memory address when we declared it).
  Suppose we dereference a wild pointer and assign a value to the memory address it is pointing at. This will lead to unexpected behaviour since we will write data at a memory block that may be free or reserved.
  Null Pointer
  To make sure that we do not have a wild pointer, we can initialize a pointer with a
  NULL value, making it a null pointer.
  char *alphabetAddress = NULL /* Null pointer */
  A null pointer points at nothing, or at a memory address that users can not access.
  Void Pointer
  A void pointer can be used to point at a variable of any data type. It can be reused to point at any data type we want to. It is declared like this:
  void *pointerVariableName = NULL;
  Since they are very general in nature, they are also known as generic pointers.
  With their flexibility, void pointers also bring some constraints. Void pointers cannot be dereferenced as any other pointer. Appropriate typecasting is necessary.
  void *pointer = NULL; int number = 54; char alphabet = "z"; pointer = &number; printf("The value of number = ", *pointer); /* Compilation Error */ /* Correct Method */ printf("The value of number = ", *(int *)pointer); /* prints "The value at number = 54" */ pointer = &alphabet; printf("The value of alphabet = ", *pointer); /* Compilation Error */ printf("The value of alphabet = ", *(char *)pointer); /* prints "The value at alphabet = z */
  Similarly, void pointers need to be typecasted for performing arithmetic operations.
  Void pointers are of great use in C. Library functions
  malloc() and
  calloc() which dynamically allocate memory return void pointers.
  qsort(), an inbuilt sorting function in C, has a function as its argument which itself takes void pointers as its argument.
  Dangling Pointer
  A dangling pointer points to a memory address which used to hold a variable. Since the address it points at is no longer reserved, using it will lead to unexpected results.
  main(){ int *ptr; ptr = (int *)malloc(sizeof(int)); *ptr = 1; printf("%d",*ptr); /* prints 1 */ free(ptr); /* deallocation */ *ptr = 5; printf("%d",*ptr); /* may or may not print 5 */ }
  Though the memory has been deallocated by
  free(ptr), the pointer to integer
  ptr still points to that unreserved memory address.
  4. Pointer Arithmetic
  We know by now that pointers are not like any other variable. They do not store any value but the address of memory blocks.
  So it should be quite clear that not all arithmetic operations would be valid with them. Would multiplying or dividing two pointers (having addresses) make sense?
  Pointers have few but immensely useful valid operations:
  - You can assign the value of one pointer to another only if they are of the same type (unless they're typecasted or one of them is
  void *).
  int ManU = 1; int *addressOfManU = &ManU; int *anotherAddressOfManU = NULL; anotherAddressOfManU = addressOfManU; /* Valid */ double *wrongAddressOfManU = addressOfManU; /* Invalid */
  2. You can only add or subtract integers to pointers.
  int myArray = {3,6,9,12,15}; int *pointerToMyArray = &myArray[0]; pointerToMyArray += 3; /* Valid */ pointerToMyArray *= 3; /* Invalid */
  When you add (or subtract) an integer (say n) to a pointer, you are not actually adding (or subtracting) n bytes to the pointer value. You are actually adding (or subtracting) n-times the size of the data type of the variable being pointed bytes.
  int number = 5; /* Suppose the address of number is 100 */ int *ptr = &number; int newAddress = ptr + 3; /* Same as ptr + 3 * sizeof(int) */
  The value stored in
  newAddress will not be 103, rather
  112.
  3. Subtraction and comparison of pointers is valid only if both are members of the same array. The subtraction of pointers gives the number of elements separating them.
  int myArray = {3,6,9,12,15}; int sixthMultiple = 18; int *pointer1 = &myArray[0]; int *pointer2 = &myArray[1]; int *pointer6 = &sixthMuliple; /* Valid Expressions */ if(pointer1 == pointer2) pointer2 - pointer1; /* Invalid Expressions if(pointer1 == pointer6) pointer2 - pointer6
  4. You can assign or compare a pointer with
  NULL.
  The only exception to the above rules is that the address of the first memory block after the last element of an array follows pointer arithmetic.
  Pointer and arrays exist together. These valid manipulations of pointers are immensely useful with arrays, which will be discussed in the next section.
  B. Arrays and Strings
  1. Why pointers and arrays?
  In C, pointers and arrays have quite a strong relationship.
  The reason they should be discussed together is because what you can achieve with array notation (
  arrayName[index]) can also be achieved with pointers, but generally faster.
  2. 1-D Arrays
  Let us look at what happens when we write
  int myArray[5];.
  Five consecutive blocks of memory starting from
  myArray[0] to
  myArray[4] are created with garbage values in them. Each of the blocks is of size 4 bytes.
  Thus, if the address of myArray[0] is
  100 (say), the address of the rest of the blocks would be
  104,
  108,
  112, and
  116.
  Have a look at the following code:
  int prime[5] = {2,3,5,7,11}; printf("Result using &prime = %d\n",&prime); printf("Result using prime = %d\n",prime); printf("Result using &prime[0] = %d\n",&prime[0]); /* Output */ Result using &prime = 6422016 Result using prime = 6422016 Result using &prime[0] = 6422016
  So,
  &prime,
  prime, and
  &prime[0] all give the same address, right? Well, wait and read because you are in for a surprise (and maybe some confusion).
  Let's try to increment each of
  &prime,
  prime, and
  &prime[0] by 1.
  printf("Result using &prime = %d\n",&prime + 1); printf("Result using prime = %d\n",prime + 1); printf("Result using &prime[0] = %d\n",&prime[0] + 1); /* Output */ Result using &prime = 6422036 Result using prime = 6422020 Result using &prime[0] = 6422020
  Wait! How come
  &prime + 1 results in something different than the other two? And why are
  prime + 1 and
  &prime[0] + 1 still equal? Let's answer these questions.
  prime and
  &prime[0] both point to the 0th element of the array
  prime. Thus, the name of an array is itself a pointer to the 0th element of the array.
  Here, both point to the first element of size 4 bytes. When you add 1 to them, they now point to the 1st element in the array. Therefore this results in an increase in the address by 4.
  &prime, on the other hand, is a pointer to an
  int array of size 5. It stores the base address of the array
  prime[5], which is equal to the address of the first element. However, an increase by 1 to it results in an address with an increase of 5 x 4 = 20 bytes.
  In short,
  arrayName and
  &arrayName[0] point to the 0th element whereas
  &arrayName points to the whole array.
  We can access the array elements using subscripted variables like this:
  int prime[5] = {2,3,5,7,11}; for( int i = 0; i < 5; i++) { printf("index = %d, address = %d, value = %d\n", i, &prime[i], prime[i]); }
  We can do the same using pointers which are always faster than using subscripts.
  int prime[5] = {2,3,5,7,11}; for( int i = 0; i < 5; i++) { printf("index = %d, address = %d, value = %d\n", i, prime + i, *(prime + i)); }
  Both methods give the output:
  index = 0, address = 6422016, value = 2 index = 1, address = 6422020, value = 3 index = 2, address = 6422024, value = 5 index = 3, address = 6422028, value = 7 index = 4, address = 6422032, value = 11
  Thus,
  &arrayName[i] and
  arrayName[i] are the same as
  arrayName + i and
  *(arrayName + i), respectively.
  3. 2-D Arrays
  Two-dimensional arrays are an array of arrays.
  int marks[5][3] = { { 98, 76, 89}, { 81, 96, 79}, { 88, 86, 89}, { 97, 94, 99}, { 92, 81, 59} };
  Here,
  marks can be thought of as an array of 5 elements, each of which is a one-dimensional array containing 3 integers. Let us work through a series of programs to understand different subscripted expressions.
  printf("Address of whole 2-D array = %d\n", &marks); printf("Addition of 1 results in %d\n", &marks +1); /* Output */ Address of whole 2-D array = 6421984 Addition of 1 results in 6422044
  Like 1-D arrays,
  &marks points to the whole 2-D array,
  marks[5][3]. Thus, incrementing to it by 1 ( = 5 arrays X 3 integers each X 4 bytes = 60) results in an increment by 60 bytes.
  printf("Address of 0th array = %d\n", marks); printf("Addition of 1 results in %d\n", marks +1); printf("Address of 0th array =%d\n", &marks[0]); printf("Addition of 1 results in %d\n", &marks[0] + 1); /* Output */ Address of 0th array = 6421984 Addition of 1 results in 6421996 Address of 0th array = 6421984 Addition of 1 results in 6421996
  If
  marks was a 1-D array,
  marks and
  &marks[0] would have pointed to the
  0th element. For a 2-D array, elements are now 1-D arrays. Hence,
  marks and
  &marks[0] point to the
  0th array (element), and the addition of 1 point to the
  1st array.
  printf("Address of 0th element of 0th array = %d\n", marks[0]); printf("Addition of 1 results in %d\n", marks[0] + 1); printf("Address of 0th element of 1st array = %d\n", marks[1]); printf("Addition of 1 results in %d\n", marks[1] + 1); /* Output */ Address of 0th element of 0th array = 6421984 Addition of 1 results in 6421988 Address of 0th element of 1st array = 6421996 Addition of 1 results in 6422000
  And now comes the difference. For a 1-D array,
  marks[0] would give the value of the 0th element. An increment by 1 would increase the value by 1.
  But, in a 2-D array,
  marks[0] points to the
  0th element of the
  0th array. Similarly,
  marks[1] points to the
  0th element of the
  1st array. An increment by 1 would point to the
  1st element in the
  1st array.
  printf("Value of 0th element of 0th array = %d\n", marks[0][0]); printf("Addition of 1 results in %d", marks[0][0] + 1); /* Output */ Value of 0th element of 0th array = 98 Addition of 1 results in 99
  This is the new part.
  marks[i][j] gives the value of the
  jth element of the
  ith array. An increment to it changes the value stored at
  marks[i][j]. Now, let us try to write
  marks[i][j] in terms of pointers.
  We know
  marks[i] + j would point to the
  ith element of the
  jth array from our previous discussion. Dereferencing it would mean the value at that address. Thus,
  marks[i][j] is the same as
  *(marks[i] + j).
  From our discussion on 1-D arrays,
  marks[i] is the same as
  *(marks + i). Thus,
  marks[i][j] can be written as
  *(*(marks + i) + j) in terms of pointers.
  Here is a summary of notations comparing 1-D and 2-D arrays.
  4. Strings
  A string is a one-dimensional array of characters terminated by a
  null(\0). When we write
  char name[] = "Srijan";, each character occupies one byte of memory with the last one always being
  \0.
  Similar to the arrays we have seen,
  name and
  &name[0] points to the
  0th character in the string, while
  &name points to the whole string. Also,
  name[i] can be written as
  *(name + i).
  /* String */ char champions[] = "Liverpool"; printf("Pointer to whole string = %d\n", &champions); printf("Addition of 1 results in %d\n", &champions + 1); /* Output */ Address of whole string = 6421974 Addition of 1 results in 6421984 printf("Pointer to 0th character = %d\n", &champions[0]); printf("Addition of 1 results in %d\n", &champions[0] + 1); /* Output */ Address of 0th character = 6421974 Addition of 1 results in a pointer to 1st character 6421975 printf("Pointer to 0th character = %d\n", champions); printf("Addition of 1 results in a pointer to 1st character %d\n", champions + 1); /* Output */ Address of 0th character = 6421974 Addition of 1 results in 6421975 printf("Value of 4th character = %c\n", champions[4]); printf("Value of 4th character using pointers = %c\n", *(champions + 4)); /* Output */ Value of 4th character = r Value of 4th character using pointers = r
  A two-dimensional array of characters or an array of strings can also be accessed and manipulated as discussed before.
  /* Array of Strings */ char top[6][15] = { "Liverpool", "Man City", "Man United", "Chelsea", "Leicester", "Tottenham" }; printf("Pointer to 2-D array = %d\n", &top); printf("Addition of 1 results in %d\n", &top + 1); /* Output */ Pointer to 2-D array = 6421952 Addition of 1 results in 6422042 printf("Pointer to 0th string = %d\n", &top[0]); printf("Addition of 1 results in %d\n", &top[0] + 1); /* Output */ Pointer to 0th string = 6421952 Addition of 1 results in 6421967 printf("Pointer to 0th string = %d\n", top); printf("Addition of 1 results in %d\n", top + 1); /* Output */ Pointer to 0th string = 6421952 Addition of 1 results in 6421967 printf("Pointer to 0th element of 4th string = %d\n", top[4]); printf("Pointer to 1st element of 4th string = %c\n", top[4] + 1); /* Output */ Pointer to 0th element of 4th string = 6422012 Pointer to 1st element of 4th string = 6422013 printf("Value of 1st character in 3rd string = %c\n", top[3][1]); printf("Same using pointers = %c\n", *(*(top + 3) + 1)); /* Output */ Value of 1st character in 3rd string = h Same using pointers = h
  5. Array of Pointers
  Like an array of
  ints and an array of
  chars, there is an array of pointers as well. Such an array would simply be a collection of addresses. Those addresses could point to individual variables or another array as well.
  The syntax for declaring a pointer array is the following:
  dataType *variableName[size]; /* Examples */ int *example1[5]; char *example2[8];
  Following the operators precedence, the first example can be read as -
  example1 is an array(
  []) of 5 pointers to
  int. Similarly,
  example2 is an array of 8 pointers to
  char.
  We can store the two-dimensional array to string
  top using a pointer array and save memory as well.
  char *top[] = { "Liverpool", "Man City", "Man United", "Chelsea", "Leicester", "Tottenham" };
  top will contain the base addresses of all the respective names. The base address of
  "Liverpool" will be stored in
  top[0],
  "Man City" in
  top[1], and so on.
  In the earlier declaration, we required 90 bytes to store the names. Here, we only require ( 58 (sum of bytes of names) + 12 ( bytes required to store the address in the array) ) 70 bytes.
  The manipulation of strings or integers becomes a lot easier when using an array of pointers.
  If we try to put
  "Leicester" ahead of
  "Chelsea", we just need to switch the values of
  top[3] and
  top[4] like below:
  char *temporary; temporary = top[3]; top[3] = top[4]; top[4] = temporary;
  Without pointers, we would have to exchange every character of the strings, which would have taken more time. That's why strings are generally declared using pointers.
  6. Pointer to Array
  Like "pointer to
  int" or "pointer to
  char", we have pointer to array as well. This pointer points to whole array rather than its elements.
  Remember we discussed how
  &arrayName points to the whole array? Well, it is a pointer to array.
  A pointer to array can be declared like this:
  dataType (*variableName)[size]; /* Examples */ int (*ptr1)[5]; char (*ptr2)[15];
  Notice the parentheses. Without them, these would be an array of pointers. The first example can be read as -
  ptr1 is a pointer to an array of 5
  int(integers).
  int goals[] = { 85,102,66,69,67}; int (*pointerToGoals)[5] = &goals; printf("Address stored in pointerToGoals %d\n", pointerToGoals); printf("Dereferncing it, we get %d\n",*pointerToGoals); /* Output */ Address stored in pointerToGoals 6422016 Dereferencing it, we get 6422016
  When we dereference a pointer, it gives the value at that address. Similarly, by dereferencing a pointer to array, we get the array and the name of the array points to the base address. We can confirm that
  *pointerToGoals gives the array
  goals if we find its size.
  printf("Size of goals[5] = %d, *pointerToGoals); /* Output */ Size of goals[5] = 20
  If we dereference it again, we will get the value stored in that address. We can print all the elements using
  pointerToGoals.
  for(int i = 0; i < 5; i++) printf("%d ", *(*pointerToGoals + i)); /* Output */ 85 102 66 69 67
  Pointers and pointer to arrays are quite useful when paired up with functions. Coming up in the next section!
  C. Functions
  1. Call by Value vs Call by Reference
  Have a look at the program below:
  #include <stdio.h> int multiply(int x, int y){ int z; z = x * y; return z; } main(){ int x = 3, y = 5; int product = multiply(x,y); printf("Product = %d\n", product); /* prints "Product = 15" */ }
  The function
  multiply() takes two
  int arguments and returns their product as
  int.
  In the function call
  multiply(x,y), we passed the value of
  x and
  y ( of
  main()), which are actual arguments, to
  multiply().
  The values of the actual arguments are passed or copied to the formal arguments
  x and
  y ( of
  multiply()). The
  x and
  y of
  multiply() are different from those of
  main(). This can be verified by printing their addresses.
  #include <stdio.h> int multiply(int x, int y){ printf("Address of x in multiply() = %d\n", &x); printf("Address of y in multiply() = %d\n", &y); int z; z = x * y; return z; } main(){ int x = 3, y = 5; printf("Address of x in main() = %d\n", &x); printf("Address of y in main() = %d\n", &y); int product = multiply(x,y); printf("Product = %d\n", product); } /* Output */ Address of x in main() = 6422040 Address of y in main() = 6422036 Address of x in multiply() = 6422000 Address of y in multiply() = 6422008 Product = 15
  Since we created stored values in a new location, it costs us memory. Wouldn't it be better if we could perform the same task without wasting space?
  Call by reference helps us achieve this. We pass the address or reference of the variables to the function which does not create a copy. Using the dereferencing operator
  *, we can access the value stored at those addresses.
  We can rewrite the above program using call by reference as well.
  #include <stdio.h> int multiply(int *x, int *y){ int z; z = (*x) * (*y); return z; } main(){ int x = 3, y = 5; int product = multiply(&x,&y); printf("Product = %d\n", product); /* prints "Product = 15" */ }
  2. Pointers as Function Arguments
  In this section, we will look at various programs where we give
  int,
  char, arrays and strings as arguments using pointers.
  ); printf("\nOperating...\n\n"); switch (operation) { case 'a': add(&a,&b); break; case 's': subtract(&a,&b); break; case 'm': multiply(&a,&b); break; case 'd': divide(&a,&b); break; default: printf("Invalid input!!!\n"); } }
  We created four functions,
  add(),
  subtract(),
  multiply() and
  divide() to perform arithmetic operations on the two numbers
  a and
  b.
  The address of
  a and
  b was passed to the functions. Inside the function using
  * we accessed the values and printed the result.
  Similarly, we can give arrays as arguments using a pointer to its first element.
  #include <stdio.h> void greatestOfAll( int *p){ int max = *p; for(int i=0; i < 5; i++){ if(*(p+i) > max) max = *(p+i); } printf("The largest element is %d\n",max); } main(){ int myNumbers[5] = { 34, 65, -456, 0, 3455}; greatestOfAll(myNumbers); /* Prints :The largest element is 3455" */ }
  Since the name of an array itself is a pointer to the first element, we send that as an argument to the function
  greatestOfAll(). In the function, we traverse through the array using loop and pointer.
  #include <stdio.h> #include <string.h> void wish(char *p){ printf("Have a nice day, %s",p); } main(){ printf("Enter your name : \n"); char name[20]; gets(name); wish(name); }
  Here, we pass in the string
  name to
  wish() using a pointer and print the message.
  3. Pointers as Function Return
  #include <stdio.h> int* multiply(int *a, int *b){ int c = *a * *b; return &c; } main(){ int a= 3, b = 5; int *c = multiply (&a,&b); printf("Product = %d",*c); }
  The function
  multiply() takes two pointers to
  int. It returns a pointer to
  int as well which stores the address where the product is stored.
  It is very easy to think that the output would be 15. But it is not!
  When
  multiply() is called, the execution of
  main() pauses and memory is now allocated for the execution of
  multiply(). After its execution is completed, the memory allocated to
  multiply() is deallocated.
  Therefore, though
  c ( local to
  main()) stores the address of the product, the data there is not guaranteed since that memory has been deallocated.
  So does that mean pointers cannot be returned by a function? No!
  We can do two things. Either store the address in the heap or global section or declare the variable to be
  static so that their values persist.
  Static variables can simply be created by using the keyword
  static before data type while declaring the variable.
  To store addresses in heap, we can use library functions
  malloc() and
  calloc() which allocate memory dynamically.
  The following programs will explain both the methods. Both methods return the output as 15.
  #include <stdio.h> #include <stdlib.h> /* Using malloc() */ int* multiply(int *a, int *b){ int *c = malloc(sizeof(int)); *c = *a * *b; return c; } main(){ int a= 3, b = 5; int *c = multiply (&a,&b); printf("Product = %d",*c); } /* Using static keyword */ #include <stdio.h> int* multiply(int *a, int *b){ static int c; c = *a * *b; return &c; } main(){ int a= 3, b = 5; int *c = multiply (&a,&b); printf("Product = %d",*c); }
  4. Pointer to Function
  Like pointer to different data types, we also have a pointer to function as well.
  A pointer to function or function pointer stores the address of the function. Though it doesn't point to any data. It points to the first instruction in the function.
  The syntax for declaring a pointer to function is:
  /* Declaring a function */ returnType functionName(parameterType1, pparameterType2, ...); /* Declaring a pointer to function */ returnType (*pointerName)(parameterType1, parameterType2, ...); pointerName = &functionName; /* or pointerName = functionName; */
  The below example will make it clearer.
  int* multiply(int *a, int *b) { int *c = malloc(sizeof(int)); *c = *a * *b; return c; } main() { int a=3,b=5; int* (*p)(int*, int*) = &multiply; /* or int* (*p)(int*, int*) = multiply; */ int *c = (*p)(&a,&b); /* or int *c = p(&a,&b); */ printf("Product = %d",*c); }
  The declaration for the pointer
  p to function
  multiply() can be read as ( following operator precedence) -
  p is a pointer to function with two
  integer pointers (
  int,
  char, and so on. Similarly, we can create an array of pointers to function.
  In this array, every element will store an address of a function, where all the functions are of the same type. That is, they have the same type and number of parameters and return types.
  We will modify a program discussed earlier in this section. We will store the addresses of
  add(),
  subtract(),
  multiply() and
  divide() in an array make a function call through subscript.
  ); void (*p[])(float* , float*) = {add,subtract,multiply,divide}; printf("\nOperating...\n\n"); switch (operation) { case 'a': p[0](&a,&b); break; case 's': p[1](&a,&b); break; case 'm': p[2](&a,&b); break; case 'd': p[3](&a,&b); break; default: printf("Invalid input!!!\n"); } }
  The declaration here can be read as -
  p is an array of pointer to functions with two
  float pointers as parameters and returning void.
  6. Pointer to Function as an Argument
  Like any other pointer, function pointers can also be passed to another function, therefore known as a callback function or called function. The function to which it is passed is known as a calling function.
  A better way to understand would be to look at
  qsort(), which is an inbuilt function in C. It is used to sort an array of integers, strings, structures, and so on. The declaration for
  qsort() is:
  void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void *));
  qsort() takes four arguments:
  - a
  voidpointer to the start of an array
  - number of elements
  - size of each element
  - a function pointer that takes in two
  voidpointers as arguments and returns an
  int
  The function pointer points to a comparison function that returns an integer that is greater than, equal to, or less than zero if the first argument is respectively greater than, equal to, or less than the second argument.
  The following program showcases its usage:
  #include <stdio.h> #include <stdlib.h> int compareIntegers(const void *a, const void *b) { const int *x = a; const int *y = b; return *x - *y; } main(){ int myArray[] = {97,59,2,83,19,97}; int numberOfElements = sizeof(myArray) / sizeof(int); printf("Before sorting - \n"); for(int i = 0; i < numberOfElements; i++) printf("%d ", *(myArray + i)); qsort(myArray, numberOfElements, sizeof(int), compareIntegers); printf("\n\nAfter sorting - \n"); for(int i = 0; i < numberOfElements; i++) printf("%d ", *(myArray + i)); } /* Output */ Before sorting - 97 59 2 83 19 97 After sorting - 2 19 59 83 97 97
  Since a function name is itself a pointer, we can write
  compareIntegers as the fourth argument.
  D. Structure
  1. Pointer to Structure
  Like integer pointers, array pointers, and function pointers, we have pointer to structures or structure pointers as well.
  struct records { char name[20]; int roll; int marks[5]; char gender; }; struct records student = {"Alex", 43, {76, 98, 68, 87, 93}, 'M'}; struct records *ptrStudent = &student;
  Here, we have declared a pointer
  ptrStudent of type
  struct records. We have assigned the address of
  student to
  ptrStudent.
  ptrStudent stores the base address of
  student, which is the base address of the first member of the structure. Incrementing by 1 would increase the address by
  sizeof(student) bytes.
  printf("Address of structure = %d\n", ptrStudent); printf("Adress of member `name` = %d\n", &student.name); printf("Increment by 1 results in %d\n", ptrStudent + 1); /* Output */ Address of structure = 6421984 Adress of member `name` = 6421984 Increment by 1 results in 6422032
  We can access the members of
  student using
  ptrStudent in two ways. Using our old friend
  * or using
  -> ( infix or arrow operator).
  With
  *, we will continue to use the
  .( dot operator) whereas with
  -> we won't need the dot operator.
  printf("Name w.o using ptrStudent : %s\n", student.name); printf("Name using ptrStudent and * : %s\n", ( *ptrStudent).name); printf("Name using ptrStudent and -> : %s\n", ptrStudent->name); /* Output */ Name without using ptrStudent: Alex Name using ptrStudent and *: Alex Name using ptrStudent and ->: Alex
  Similarly, we can access and modify other members as well. Note that the brackets are necessary while using
  * since the dot operator(
  .) has higher precedence over
  *.
  2. Array Of Structure
  We can create an array of type
  struct records and use a pointer to access the elements and their members.
  struct records students[10]; /* Pointer to the first element ( structure) of the array */ struct records *ptrStudents1 = &students; /* Pointer to an array of 10 struct records */ struct records (*ptrStudents2)[10] = &students;
  Note that
  ptrStudent1 is a pointer to
  student[0] whereas
  ptrStudent2 is a pointer to the whole array of 10
  struct records. Adding 1 to
  ptrStudent1 would point to
  student[1].
  We can use
  ptrStudent1 with a loop to traverse through the elements and their members.
  for( int i = 0; i < 10; i++) printf("%s, %d\n", ( ptrStudents1 + i)->name, ( ptrStudents1 + i)->roll);
  3. Pointer to Structure as an Argument
  We can also pass the address of a structure variable to a function.
  #include <stdio.h> struct records { char name[20]; int roll; int marks[5]; char gender; }; main(){ struct records students = {"Alex", 43, {76, 98, 68, 87, 93}, 'M'}; printRecords(&students); } void printRecords( struct records *ptr){ printf("Name: %s\n", ptr->name); printf("Roll: %d\n", ptr->roll); printf("Gender: %c\n", ptr->gender); for( int i = 0; i < 5; i++) printf("Marks in %dth subject: %d\n", i, ptr->marks[i]); } /* Output */ Name: Alex Roll: 43 Gender: M Marks in 0th subject: 76 Marks in 1th subject: 98 Marks in 2th subject: 68 Marks in 3th subject: 87 Marks in 4th subject: 93
  Note that the structure
  struct records is declared outside
  main(). This is to ensure that it is available globally and
  printRecords() can use it.
  If the structure is defined inside
  main(), its scope will be limited to
  main(). Also structure must be declared before the function declaration as well.
  Like structures, we can have pointers to unions and can access members using the arrow operator (
  ->).
  E. Pointer to Pointer
  So far we have looked at pointer to various primitive data types, arrays, strings, functions, structures, and unions.
  The automatic question that comes to the mind is – what about pointer to pointer?
  Well, good news for you! They too exist.
  int var = 6; int *ptr_var = &var; printf("Address of var = %d\n", ptr_var); printf("Address of ptr_var = %d\n", &ptr_var); /* Output */ Address of var = 6422036 Address of ptr_var = 6422024
  To store the address of
  int variable
  var, we have the pointer to
  int
  ptr_var. We would need another pointer to store the address of
  ptr_var.
  Since
  ptr_var is of type
  int *, to store its address we would have to create a pointer to
  int *. The code below shows how this can be done.
  int * *ptr_ptrvar = &ptr_var; /* or int* *ppvar or int **ppvar */
  We can use
  ptr_ptrvar to access the address of
  ptr_var and use double dereferencing to access var.
  printf("Address of ptr_var = %d\n", ptr_ptrvar); printf("Address of var = %d\n", *ptr_ptrvar); printf("Value at var = %d\n", *(*ptr_ptrvar)); /* Output */ Address of ptr_var = 6422024 Address of var = 6422036 Value at var = 6
  It is not required to use brackets when dereferencing
  ptr_ptrvar. But it is a good practice to use them. We can create another pointer
  ptr_ptrptrvar, which will store the address of
  ptr_ptrvar.
  Since
  ptr_ptrvar is of type
  int**, the declaration for
  ptr_ptrptrvar will be
  int** *ptr_ptrptrvar = &ptr_ptrvar;
  We can again access
  ptr_ptrvar,
  ptr_var and
  var using
  ptr_ptrptrvar.
  printf("Address of ptr_ptrvar = %d\n", ptr_ptrptrvar); printf("Value at ptr_ptrvar = %d\n",*ptr_ptrptrvar); printf("Address of ptr_var = %d\n", *ptr_ptrptrvar); printf("Value at ptr_var = %d\n", *(*ptr_ptrptrvar)); printf("Address of var = %d\n", *(*ptr_ptrptrvar)); printf("Value at var = %d\n", *(*(*ptr_ptrptrvar))); /* Output */ Address of ptr_ptrvar = 6422016 Value at ptr_ptrvar = 6422024 Address of ptr_var = 6422024 Value at ptr_var = 6422036 Address of var = 6422036 Value at var = 6
  If we change the value at any of the pointer(s) using
  ptr_ptrptrvar or
  ptr_ptrvar, the pointer(s) will stop pointing to the variable.
  Conclusion
  Phew! Yeah, we're finished. We started from pointers and ended with pointers (in a way). Don't they say that the curve of learning is a circle!
  Try to recap all the sub-topics that you read. If you can recollect them, well done! Read the ones you can't remember again.
  This article is done, but you shouldn't be done with pointers. Play with them. Next, you can look into Dynamic Memory Allocation to get to know pointers better.
  Stay home, stay safe.