Explain Pointer Operations in C with Examples

«
»
This C Tutorial Explains Different Pointer Operations in C Programming with examples.

Let’s start by considering the fragment of code given below:

    char ch = 'A';
    char *cp = &ch;
 
    printf("character ch is %c\n", *cp);
 
    cp++; 	/* cp, pointer-to-char is incremented by 1 */
    printf("After cp++, where does cp point to?\n");

Since type ‘char’ takes one byte, when ‘cp’, pointer-to-char, is incremented by 1, it’s value, address of character ch, gets incremented by 1, that is ‘cp’ points to next byte. What does that byte contain whether a character or some other value? We can’t predict this simply by bit pattern of that byte. As we know we require type of value to predict what’s contained in that location. Let’s see another fragment of code below:

advertisement
    int goggles = 1885;
    int *ip = &goggles;    /* ip is initialized address of int goggles */
 
    printf("value of goggles is %d\n", *ip);
    ip++; 	/* ip is incremented by 1 */
    printf("After ip++, where does ip point to?\n");

As we know that address of a variable of type which is allocated more than one byte of storage, viz, int, float, double etc, is the addrerss of first byte of storage. Since type ‘int’ on linux system takes 4 bytes, does pointer-to-int ‘ip’ after incremented by 1 points to somewhere inside the integer or some elsewhere?

Actually, when we increment a pointer, say, for example, by 1, integer 1 is scaled to proper size before addition to the pointer. Meaning that by what integer value pointer is to be incremented by is multiplied by size of type pointed to by pointer. For example, here ‘ip’ points to integer. when ‘ip’ is incremented by 1, 1 is multiplied by size of type int i.e. 4 bytes. So, next time when ‘ip’ is incremented by 4, 4 is scaled to 16 bytes before adding to ‘ip’. For example,

    if 'ip' has address 1000, and is incremented by 10,
    then 10 is scaled to 40 bytes before addition to the 'ip'.
    After addition, 'ip' points to address 1040.

Therefore, when performing addition on a pointer, size of a pointer increments by no. of storage units and not by no. of bytes. This is the beauty of scaling that pointer arithmetic doesn’t depend on type pointer is pointing to. Meaning that if a pointer points to character, when incremented by 1, points to next character, or if it points to float, points to next float when incremented by 1 and so on.

Till now, we considered scalar varibles for pointer arithmetic. But C standard defines two arithmetic forms for pointers, first is as:

    <b>pointer +- pointer;</b>

Remember here that both pointers must point to elements of the same array. For example:

advertisement
/*
 * two_ptrs.c -- Program shows manipulation of an array using two pointers
 * to array
 */
#include <stdio.h>
#define FAMILIES 10
 
int main(void) 
{
    int fam_mem[FAMILIES];
    int *ip1, *ip2;
 
    printf("Read in from user, family members for %d families...\n",
           FAMILIES);
 
    for (ip1 = &fam_mem[0], ip2 = &fam_mem[FAMILIES]; ip1 < ip2; ip1++)
        scanf("%d", ip1);
 
    return 0;
}

What happens here is that *ip1 and *ip2, during initialization step of for statement, take addresses respectively of first and one beyond last element of array fam_mem[FAMILIES], then test is performed to see if *ip1 has reached one beyond the last element of fam_mem[FAMILIES], and reads into the location, pointed to by pointer, no. of family members from the user and then pointer *ip is incremented and next iteration follows until pointer *ip1 has reached address of *ip2, i.e. address of one beyond the last element of fam_mem[FAMILIES].

Remember that indexing in arrays in C begins with 0 and end up with size of arrays minus 1. That is an index as size of an array defines one beyond the last element. Standard defines that effect of a addition and a subtraction is undefined if the result points to anything earlier than the first element of the array or if it points to any element more than one beyond the last element of the array. However, it’s legal for pointer to go one element past the end of array, but indirection may not be performed on it then. For example:

/*
 * access_valid_invalid_indices.c -- Program explores what happens when
 * access array beyond its valid indices
 */
#include <stdio.h>
#define FAMILIES 5
 
int main(void)
{
    int fam_mem[FAMILIES];
    int *ip1, *ip2;
 
    printf("Read in from user, family members for %d families...\n",
           FAMILIES);
 
    for (ip1 = &fam_mem[0], ip2 = &fam_mem[FAMILIES]; ip1 < ip2; ip1++)
        scanf("%d", ip1);
    printf("\n");
 
    printf("No. of family members for %d Families is as:\n", FAMILIES);
 
    for (ip1 = &fam_mem[0], ip2 = &fam_mem[FAMILIES]; ip1 < ip2; ip1++)
        printf("%d\t", *ip1);
    printf("\n");
 
    printf("\nAccessing array fam_mem[%d] with valid indices\n\n",
           FAMILIES);
 
    ip1 = &fam_mem[0];
    ip1 + 4;
 
    printf("After \"ip1 = &fam_mem[0]\" and \"ip1 + 4\"\nip1 + 4 contains"
           " address %p and value at that address is %d\n",
           ip1 + 4, *(ip1 + 4));
 
    ip2 - 5;
    printf("After \"ip2 = &fam_mem[%d]\" and \"ip2 - 5\"\nip2 - 5 contains"
           " address %p and value at that address is %d\n",
           FAMILIES, ip2 - 5, *(ip2 - 5));
 
    printf("\nLet's access array fam_mem[%d] with invalid indices\n\n",
           FAMILIES);
 
    ip1 = &fam_mem[0];
    ip1 - 1;
    /* *ip1 points to one before first element of array fam_mem[] */
 
    printf("After \"ip1 = &fam_mem[0]\" and \"ip1 - 1\"\nip1 - 1 contains"
           " address %p and value at that address is %d\n",
           (ip1 - 1), *(ip1 - 1));
 
    ip2 = &fam_mem[FAMILIES];
    printf("After \"ip2 = &fam_mem[%d]\"\n*ip2 contains address %p and "
           "value at that address is %d\n", FAMILIES, ip2, *ip2);
    printf("\n");
    return 0;
}

Output of the program below:

Read in from user, family members for 5 families...
6
3
10
7
8
 
No. of family members for 5 Families is as:
6	3	10	7	8	
 
Accessing array fam_mem[5] with valid indices
 
After "ip1 = &fam_mem[0]" and "ip1 + 4"
ip1 + 4 contains address 0x7fff26173f00 and value at that address is 8
After "ip2 = &fam_mem[5]" and "ip2 - 5"
ip2 - 5 contains address 0x7fff26173ef0 and value at that address is 6
 
Let's access array fam_mem[5] with invalid indices
 
After "ip1 = &fam_mem[0]" and "ip1 - 1"
ip1 - 1 contains address 0x7fff26173eec and value at that address is 0
After "ip2 = &fam_mem[5]"
*ip2 contains address 0x7fff26173f04 and value at that address is 0

Let’s consider the second form of pointer arithmetic that C Standard Defines:

advertisement
    pointer - pointer;

This form has meaning only when both pointer points to elements of the same array. Result of subtracting one pointer from another, when two points to elements of the same array, is of the type of signed integral ptrdiff_t. For example, consider the fragment of code below,

    float marks[6] = {23, 55.5, 43, 87.5, 91, 66.45};
    float *fp1, *fp2;
 
    fp1 = &marks[3];
    fp2 = &marks[5];
 
    fp1 - fp2; /* ? */
    fp2 - fp1; /* ? */

Result of pointer difference is measured in distance between the elements and not bytes. In above code, fp2 – fp1 is +2, even float takes 4 bytes on Linux system because distance is scaled/divided by the size of float. Again, scaling makes pointer subtraction independent of type of data pointer is pointing to.

Now, is fp1 – fp2 i.e. -2 valid? Yes, as long as two points to elements of the same array.

O key! We now consider following fragment of code,

     float rainfall[5] = {2.1, 0.33, 3.12, 1.5, 2.22};
     float marks[5] = {56.6, 66.32, 87.5, 91.0, 34.9};
 
     float *fp1, *fp2;
 
     fp1 - fp2; /* ? */
     fp2 - fp1; /* ? */

Subtraction of two pointers pointing to different arrays worth for nothing as programmer has no way of knowing where two arrays have been allocated relative to one another. Without this knowledge, distance from one pointer from another has no meaning.

Remember that most compilers don’t check whether result of pointer expression falls within legal bounds. Therefore, it is up to the programmer to make sure it does.

Relational operations on Pointers

Relational operations are also constrained on pointers, meaning that we can compare values of two pointers using operators <, <=, > and >= if they belong to elements of same array. For example: We have seen above that

    for (fp1 = &marks[0]; fp1 < &marks[6]; fp1++) 
        scanf("%f", fp1);

in which we compared ‘fp1’, pointer-to-float, with pointer constant &marks[6]. Once ‘fp1’ incremented to one beyond the last element of array marks[6], for loop terminates.

Comparing the values of two arbitrary pointers is not defined by the standard. However, we can compare values of any two pointers for equality or inequality because result doesn’t depend on where the compiler has allocated the data. We try rewrite the above for loop using ‘!=’ operator,

    for (fp1 = &marks[0]; fp1 != &marks[6]; fp1++)
        printf("%f\t", *fp1);
 
    if (fp1 == &marks[0])
        *fp1 = 65.0;

Sanfoundry Global Education & Learning Series – 1000 C Tutorials.

If you wish to look at all C Tutorials, go to C Tutorials.

advertisement
Manish Bhojasia, a technology veteran with 20+ years @ Cisco & Wipro, is Founder and CTO at Sanfoundry. He is Linux Kernel Developer & SAN Architect and is passionate about competency developments in these areas. He lives in Bangalore and delivers focused training sessions to IT professionals in Linux Kernel, Linux Debugging, Linux Device Drivers, Linux Networking, Linux Storage, Advanced C Programming, SAN Storage Technologies, SCSI Internals & Storage Protocols such as iSCSI & Fiber Channel. Stay connected with him @ LinkedIn