Pointers in C Explained – They're Not as Difficult as You Think (2023)

/ #C Programming
Pointers in C Explained – They're Not as Difficult as You Think (1)
Shrijan
Pointers in C Explained – They're Not as Difficult as You Think (2)

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
  1. What exactly are pointers?
  2. Definition and Notation
  3. Some Special Pointers
  4. Pointer Arithmetic
B. Arrays and Strings
  1. Why pointers and arrays?
  2. 1-D Arrays
  3. 2-D Arrays
  4. Strings
  5. Array of Pointers
  6. Pointer to Array
C. Functions
  1. Call by Value v/s Call by Reference
  2. Pointers as Function Arguments
  3. Pointers as Function Return
  4. Pointer to Function
  5. Array Of Pointers to Functions
  6. Pointer to Function as an Argument
D. Structure
  1. Pointer to Structure
  2. Array of Structure
  3. Pointer to Structure as an Argument
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;

Pointers in C Explained – They're Not as Difficult as You Think (3)

A block of memory is reserved by the compiler to hold anintvalue. The name of this block isdigitand the value stored in this block is42.

Now, to remember the block, it is assigned with anaddressor 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) oraddress ofoperator.

printf("The address of digit = %d.",&digit); /* prints "The address of digit = 24650. */

We can get the value of the variabledigitfrom its address using another operator*(asterisk), called theindirectionordereferencingorvalue at addressoperator.

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 ourdigitvariable, this can be written like this:

int *addressOfDigit = &digit;

or like this:

int *addressOfDigit;addressOfDigit= &digit;

Pointers in C Explained – They're Not as Difficult as You Think (4)

This can be read as -A pointer toint(integer)addressOfDigitstores theaddress of(&) digitvariable.

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,intwas the data type ofdigit.

Itdoes notmean thataddressOfDigitwill store a value of typeint. An integer pointer (likeaddressOfDigit) canonlystore the address of variables of integer type.

int variable1;int variable2;char variable3;int *addressOfVariables;

*– A pointer variable is aspecialvariable 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 ofvariable1andvariable2to the integer pointeraddressOfVariablesbut not tovariable3since it is of typechar. We will need a character pointer variable to store its address.

We can use ouraddressOfDigitpointer variable to print the address and the value ofdigitas 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,*addressOfDigitcan be read asthe value at the address stored inaddressOfDigit.

Notice we used%das theformat identifierforaddressOfDigit. 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 anentirely correctway, 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 = 000000000061FE00Address using %d = 6422016Address 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 pointeralphabetAddress, we did not initialize it.

Such pointers are known aswild pointers. They store a garbage value (that is, memory address) of a byte that we don't know is reserved or not (rememberint 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 aNULLvalue, making it anull pointer.

char *alphabetAddress = NULL /* Null pointer */

A null pointer points at nothing, or at a memory address that users can not access.

Void Pointer

Avoid pointercan 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 verygeneralin nature, they are also known asgeneric pointers.

With their flexibility, void pointers also bring some constraints. Void pointerscannotbe dereferenced as any other pointer. Appropriatetypecastingis 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 functionsmalloc()andcalloc()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 whichused tohold 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 byfree(ptr), the pointer to integerptrstill 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 usefulvalidoperations:

  1. 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 isvoid *).
int ManU = 1;int *addressOfManU = &ManU;int *anotherAddressOfManU = NULL;anotherAddressOfManU = addressOfManU; /* Valid */double *wrongAddressOfManU = addressOfManU; /* Invalid */

2. You can onlyadd or subtract integersto 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 arenotactually 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 pointedbytes.

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 innewAddresswillnotbe 103, rather112.

3.Subtraction and comparison of pointersis 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 Expressionsif(pointer1 == pointer6)pointer2 - pointer6

4. You can assign or compare a pointer withNULL.

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 theyshouldbe 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 writeint myArray[5];.

Fiveconsecutiveblocks of memory starting frommyArray[0]tomyArray[4]are created with garbage values in them. Each of the blocks is of size 4 bytes.

Thus, if the address of myArray[0] is100(say), the address of the rest of the blocks would be104,108,112, and116.

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 = 6422016Result using prime = 6422016Result 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 = 6422036Result using prime = 6422020Result using &prime[0] = 6422020

Wait! How come&prime + 1results in something different than the other two? And why areprime + 1and&prime[0] + 1still equal? Let's answer these questions.

primeand&prime[0]both point to the 0th element of the arrayprime. Thus, thename 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, isa pointer to anintarray of size 5. It stores the base address of the arrayprime[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,arrayNameand&arrayName[0]point to the 0th element whereas&arrayNamepoints to the whole array.

Pointers in C Explained – They're Not as Difficult as You Think (5)

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 arealwaysfaster 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 = 2index = 1, address = 6422020, value = 3index = 2, address = 6422024, value = 5index = 3, address = 6422028, value = 7index = 4, address = 6422032, value = 11

Thus,&arrayName[i]andarrayName[i]are the same asarrayName + iand*(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,markscan 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 = 6421984Addition of 1 results in 6422044

Like 1-D arrays,&markspoints 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 = 6421984Addition of 1 results in 6421996Address of 0th array = 6421984Addition of 1 results in 6421996

Ifmarkswas a 1-D array,marksand&marks[0]would have pointed to the0thelement.For a 2-D array, elements are now 1-D arrays. Hence,marksand&marks[0]point to the0tharray (element), and the addition of 1 point to the1starray.

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 = 6421984Addition of 1 results in 6421988Address of 0th element of 1st array = 6421996Addition 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 the0thelement of the0tharray. Similarly,marks[1]points to the0thelement of the1starray. An increment by 1 would point to the1stelement in the1starray.

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 = 98Addition of 1 results in 99

This is the new part.marks[i][j]gives the value of thejthelement of theitharray. An increment to it changes the value stored atmarks[i][j]. Now, let us try to writemarks[i][j]in terms of pointers.

We knowmarks[i] + jwould point to theithelement of thejtharray 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.

Expression 1-D Array 2-D Array
&arrayName points to the address of whole array
adding 1 increases the address by 1 x sizeof(arrayName)
points to the address of whole array
adding 1 increases the address by 1 x sizeof(arrayName)
arrayName points to the 0th element
adding 1 increases the address to 1st element
points to the 0th element (array)
adding 1 increases the address to 1st element (array)
&arrayName[i] points to the the ith element
adding 1 increases the address to (i+1)th element
points to the ith element (array)
adding 1 increases the address to the (i+1)th element (array)
arrayName[i] gives the value of the ith element
adding 1 increases the value of the ith element
points to the 0th element of the ith array
adding 1 increases the address to 1st element of the ith array
arrayName[i][j] Nothing gives the value of the jth element of the ith array
adding 1 increases the value of the jth element of the ith array
Pointer Expression To Access The Elements *( arrayName + i) *( *( arrayName + i) + j)

4. Strings

A string is a one-dimensional array of characters terminated by anull(\0). When we writechar name[] = "Srijan";, each character occupies one byte of memory with the last one always being\0.

Similar to the arrays we have seen,nameand&name[0]points to the0thcharacter in the string, while&namepoints 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 = 6421974Addition of 1 results in 6421984printf("Pointer to 0th character = %d\n", &champions[0]);printf("Addition of 1 results in %d\n", &champions[0] + 1);/* Output */Address of 0th character = 6421974Addition of 1 results in a pointer to 1st character 6421975printf("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 = 6421974Addition of 1 results in 6421975printf("Value of 4th character = %c\n", champions[4]);printf("Value of 4th character using pointers = %c\n", *(champions + 4));/* Output */Value of 4th character = rValue 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 = 6421952Addition of 1 results in 6422042printf("Pointer to 0th string = %d\n", &top[0]);printf("Addition of 1 results in %d\n", &top[0] + 1); /* Output */Pointer to 0th string = 6421952Addition of 1 results in 6421967printf("Pointer to 0th string = %d\n", top);printf("Addition of 1 results in %d\n", top + 1); /* Output */Pointer to 0th string = 6421952Addition of 1 results in 6421967printf("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 = 6422012Pointer to 1st element of 4th string = 6422013printf("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 = hSame using pointers = h

5. Array of Pointers

Like an array ofints and an array ofchars, 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 apointer arrayis the following:

dataType *variableName[size];/* Examples */int *example1[5];char *example2[8];

Following theoperators precedence, the first example can be read as -example1is an array([]) of 5 pointers toint. Similarly,example2is an array of 8 pointers tochar.

We can store the two-dimensional array to stringtopusing a pointer array and save memory as well.

char *top[] = { "Liverpool", "Man City", "Man United", "Chelsea", "Leicester", "Tottenham" };

topwill contain the base addresses of all the respective names. The base address of"Liverpool"will be stored intop[0],"Man City"intop[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 oftop[3]andtop[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 toint" or "pointer tochar", we have pointer to array as well. This pointer points to whole array rather than its elements.

Remember we discussed how&arrayNamepoints 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 -ptr1is a pointer to an array of 5int(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 6422016Dereferencing 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*pointerToGoalsgives the arraygoalsif 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 usingpointerToGoals.

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 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 functionmultiply()takes twointarguments and returns their product asint.

In the function callmultiply(x,y), we passed the value ofxandy( ofmain()), which areactual arguments, tomultiply().

The values of the actual arguments are passed or copied to theformal arguments xandy( ofmultiply()). Thexandyofmultiply()are different from those ofmain(). This can be verified by printing their addresses.

#include 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() = 6422040Address of y in main() = 6422036Address of x in multiply() = 6422000Address of y in multiply() = 6422008Product = 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 referencehelps 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 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 giveint,char, arrays and strings as arguments using pointers.

#include void add(float *a, float *b){ float c = *a + *b; printf("Addition gives %.2f\n",c);}void subtract(float *a, float *b){ float c = *a - *b; printf("Subtraction gives %.2f\n",c);}void multiply(float *a, float *b){ float c = *a * *b; printf("Multiplication gives %.2f\n",c);}void divide(float *a, float *b){ float c = *a / *b; printf("Division gives %.2f\n",c);}main(){ printf("Enter two numbers :\n"); float a,b; scanf("%f %f",&a,&b); printf("What do you want to do with the numbers?\nAdd : a\nSubtract : s\nMultiply : m\nDivide : d\n"); char operation = '0'; scanf(" %c",&operation); 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()anddivide()to perform arithmetic operations on the two numbersaandb.

The address ofaandbwas 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 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 functiongreatestOfAll(). In the function, we traverse through the array using loop and pointer.

#include #include 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 stringnametowish()using a pointer and print the message.

3. Pointers as Function Return

#include 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 functionmultiply()takes two pointers toint. It returns a pointer tointas 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!

Whenmultiply()is called, the execution ofmain()pauses and memory is now allocated for the execution ofmultiply(). After its execution is completed, the memory allocated tomultiply()is deallocated.

Therefore, thoughc( local tomain()) stores the address of the product, the data there isnot guaranteedsince 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 bestaticso that their values persist.

Static variables can simply be created by using thekeyword staticbefore data type while declaring the variable.

To store addresses in heap, we can use library functionsmalloc()andcalloc()which allocate memory dynamically.

The following programs will explain both the methods. Both methods return the output as 15.

#include #include /* 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 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 orfunction pointerstores 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 pointerpto functionmultiply()can be read as ( followingoperator precedence) -pis a pointer to function with twointeger pointers ( or two pointers toint) as parameters and returning a pointer toint.

Since the name of the function is also a pointer to the function, the use of&is not necessary. Also removing*from the function call doesn't affect the program.

5. Array of Pointers to Functions

We have already seen how to create an array of pointers toint,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 ofadd(),subtract(),multiply()anddivide()in an array make a function call through subscript.

#include void add(float *a, float *b){ float c = *a + *b; printf("Addition gives %.2f\n",c);}void subtract(float *a, float *b){ float c = *a - *b; printf("Subtraction gives %.2f\n",c);}void multiply(float *a, float *b){ float c = *a * *b; printf("Multiplication gives %.2f\n",c);}void divide(float *a, float *b){ float c = *a / *b; printf("Division gives %.2f\n",c);}main(){ printf("Enter two numbers :\n"); float a,b; scanf("%f %f",&a,&b); printf("What do you want to do with the numbers?\nAdd : a\nSubtract : s\nMultiply : m\nDivide : d\n"); char operation = '0'; scanf(" %c",&operation); 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 -pis an array of pointer to functions with twofloatpointers 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 acallback functionorcalled function. The function to which it is passed is known as acalling function.

A better way to understand would be to look atqsort(), which is an inbuilt function in C. It is used to sort an array of integers, strings, structures, and so on. The declaration forqsort()is:

void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void *, const void *));

qsort()takes four arguments:

  1. avoidpointer to the start of an array
  2. number of elements
  3. size of each element
  4. a function pointer that takes in twovoidpointers as arguments and returns anint

The function pointer points to acomparison functionthat 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 #include 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 97After sorting -2 19 59 83 97 97

Since a function name is itself a pointer, we can writecompareIntegersas 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 pointerptrStudentof typestruct records. We have assigned the address ofstudenttoptrStudent.

ptrStudentstores the base address ofstudent, which is the base address of the first member of the structure. Incrementing by 1 would increase the address bysizeof(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 = 6421984Adress of member `name` = 6421984Increment by 1 results in 6422032

We can access the members ofstudentusingptrStudentin 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: AlexName using ptrStudent and *: AlexName 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 typestruct recordsand 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 thatptrStudent1is a pointer tostudent[0]whereasptrStudent2is a pointer to the whole array of 10struct records. Adding 1 toptrStudent1would point tostudent[1].

We can useptrStudent1with 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 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: AlexRoll: 43Gender: MMarks in 0th subject: 76Marks in 1th subject: 98Marks in 2th subject: 68Marks in 3th subject: 87Marks in 4th subject: 93

Note that the structurestruct recordsis declared outsidemain(). This is to ensure that it is available globally andprintRecords()can use it.

If the structure is defined insidemain(), its scope will be limited tomain(). Also structuremust bedeclared 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 = 6422036Address of ptr_var = 6422024

To store the address ofintvariablewas, we have the pointer toint ptr_var. We would need another pointer to store the address ofptr_var.

Sinceptr_varis of typeint *, to store its address we would have to create a pointer toint *. The code below shows how this can be done.

int * *ptr_ptrvar = &ptr_var; /* or int* *ppvar or int **ppvar */

We can useptr_ptrvarto access the address ofptr_varand 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 = 6422024Address of var = 6422036Value at var = 6

It is not required to use brackets when dereferencingptr_ptrvar. But it is a good practice to use them. We can create another pointerptr_ptrptrvar, which will store the address ofptr_ptrvar.

Sinceptr_ptrvaris of typeint**, the declaration forptr_ptrptrvarwill be

int** *ptr_ptrptrvar = &ptr_ptrvar;

We can again accessptr_ptrvar,ptr_varandwasusingptr_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 = 6422016Value at ptr_ptrvar = 6422024Address of ptr_var = 6422024Value at ptr_var = 6422036Address of var = 6422036Value at var = 6

Pointers in C Explained – They're Not as Difficult as You Think (6)

If we change the value at any of the pointer(s) usingptr_ptrptrvarorptr_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 thatthe 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 intoDynamic Memory Allocationto get to know pointers better.

Stay home, stay safe.

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

ADVERTISEMENT

Pointers in C Explained – They're Not as Difficult as You Think (7)
Shrijan

Sachin. Cricket. Dhoni. De Villiers. Buttler. In that order.

If you read this far, tweet to the author to show them you care.

Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers.Get started

ADVERTISEMENT

FAQs

What is the best explanation of pointers in C? ›

The Pointer in C, is a variable that stores address of another variable. A pointer can also be used to refer to another pointer function. A pointer can be incremented/decremented, i.e., to point to the next/ previous memory location.

Why are pointers in C so confusing? ›

Second thing which make pointers confusing is that pointers hold no information about what they point to. And the most complicated thing is it's syntax speciaaly in case of crossovers. Pointers are addresses of central memory cases . So , for each data used in programs we Can also manage their addresses .

Are pointers in C hard? ›

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.

What are pointers explained easy? ›

What is a Pointer? A pointer is a variable that stores a memory address. Pointers are used to store the addresses of other variables or memory items. Pointers are very useful for another type of parameter passing, usually referred to as Pass By Address.

What are the disadvantages of pointers in C? ›

Disadvantages of Pointers are :-
  • Pointer are slower than normal variables.
  • Uninitialized pointer might cause segmentation fault.
  • Dynamically allocated block needs to be freed explicity. ...
  • If pointer bugs are updated with incorrect values, it migh lead to memory corruption.
  • Basically, pointer bugs are difficult to handle.

What are the advantages of pointers in C? ›

Using pointer in C programming has following advantages:
  • Pointers provide direct access to memory.
  • Pointers provide a way to return multiple values from a user defined function without using return keyword.
  • Pointers reduce the storage space and complexity of programs.
  • Pointers reduce the execution time of programs.

Why are pointers bad? ›

The use of pointers can lead to confusion of ownership which can directly or indirectly lead to memory leaks. Even if object ownership is well managed simple (and difficult to find) bugs can also lead to memory leaks.

What is the common problem with pointers? ›

What are the major problems with pointers? Two common problems associated with pointers are undefined behaviour and garbage values. Undefined behavior occurs when two pointers are pointing to the same memory location and we free one of the pointers. Garbage values in the memory are those which cannot be accessed.

Why do people struggle with pointers? ›

There are two types of people who struggle with the concept of pointers: People who are new to programming, People who have used languages that don't permit the programmer to directly reference memory locations (essentially most languages besides C/C++ or Pascal)

Should I avoid pointers in C? ›

If you don't need to use pointers, then there's no reason to use pointers. Why is this even a question? In reality, it's pretty much impossible to write a functioning C program without pointers. Even the classical Hello World program uses them.

Is it easy to learn pointers? ›

They are not that difficult, There is however something that needs to click, and depending on the language you are using, the notation also doesn't help. Mostly, pointers are tricky because you're used to think in things. Everything in a computer is really stored in memory. Each bit / byte in memory has an address.

Do pointers make code faster? ›

If you use them carefully, pointers can reduce the amount of program code you need to write, thereby increasing your program's efficiency and enabling you to use less memory. (Your program can run faster because it does not have to duplicate the data in memory).

What is the main purpose of pointers? ›

Pointers are used to store and manage the addresses of dynamically allocated blocks of memory. Such blocks are used to store data objects or arrays of objects.

Why are pointers so powerful? ›

However, "pointer" is also the most complex and difficult feature in C/C++ language. Pointers are extremely powerful because they allows you to access addresses and manipulate their contents. But they are also extremely complex to handle. Using them correctly, they could greatly improve the efficiency and performance.

What is everything about pointers in C? ›

The pointer in C language is a variable which stores the address of another variable. This variable can be of type int, char, array, function, or any other pointer. The size of the pointer depends on the architecture. However, in 32-bit architecture the size of a pointer is 2 byte.

What is the concept of pointers in C with example? ›

A pointer is a variable that stores the address of another variable. Unlike other variables that hold values of a certain type, pointer holds the address of a variable. For example, an integer variable holds (or you can say stores) an integer value, however an integer pointer holds the address of a integer variable.

What is the definition of this pointer in C? ›

The this pointer is a pointer accessible only within the nonstatic member functions of a class , struct , or union type. It points to the object for which the member function is called. Static member functions don't have a this pointer.

What is the function of pointers in C? ›

Function pointers in C can be used to create function calls to which they point. This allows programmers to pass them to functions as arguments. Such functions passed as an argument to other functions are also called callback functions.

What is the best explanation of pointers in C++? ›

In C++, every variable has its unique address or location in the computer's memory, and this special address is called memory address. You can define a pointer as the variable that holds the memory address of some other variable. It allows the developer to deal with the memory.

References

Top Articles
Latest Posts
Article information

Author: Stevie Stamm

Last Updated: 09/27/2023

Views: 6225

Rating: 5 / 5 (60 voted)

Reviews: 83% of readers found this page helpful

Author information

Name: Stevie Stamm

Birthday: 1996-06-22

Address: Apt. 419 4200 Sipes Estate, East Delmerview, WY 05617

Phone: +342332224300

Job: Future Advertising Analyst

Hobby: Leather crafting, Puzzles, Leather crafting, scrapbook, Urban exploration, Cabaret, Skateboarding

Introduction: My name is Stevie Stamm, I am a colorful, sparkling, splendid, vast, open, hilarious, tender person who loves writing and wants to share my knowledge and understanding with you.