Submitted by Anonymous (not verified) on Sun, 03/10/2013 - 19:31

Pointer Arithmetic

In C, you can move the position of a pointer by adding or subtracting integers to or from the pointer. For example, given a character pointer variable ptr_str, the following expression

ptr_str + 1

indicates to the compiler to move to the memory location that is one byte away from the current position of ptr_str.

Note that for pointers of different data types, the integers added to or subtracted from the pointers have different scalar sizes. In other words, adding 1 to (or subtracting 1 from) a pointer is not instructing the compiler to add (or subtract) one byte to the address, but to adjust the address so that it skips over one element of the type of the pointer. You'll see more details in the following sections.
The Scalar Size of Pointers

The general format to change the position of a pointer is

pointer_name + n

Here n is an integer whose value can be either positive or negative. pointer_name is the name of a pointer variable that has the following declaration:

data_type_specifier *pointer_name;

When the C compiler reads the pointer_name + n expression, it interprets the expression as

pointer_name + n * sizeof(data_type_specifier)

Note that the sizeof operator is used to obtain the number of bytes that a specified data type can have. Therefore, for the char pointer variable ptr_str, the ptr_str + 1 expression actually means

ptr_str + 1 * sizeof(char).

Because the size of a character is one byte long, ptr_str + 1 tells the compiler to move to the memory location that is 1 byte after the current location referenced by the pointer.

The program in Listing 16.1 shows how the scalar sizes of different data types affect the offsets added to or subtracted from pointers.
 

TYPE
Listing 16.1. Moving pointers of different data types.


1:  /* 16L01.c: Pointer arithmetic */
2:  #include <stdio.h>
3:
4:  main()
5:  {
6:     char *ptr_ch;
7:     int *ptr_int;
8:     double *ptr_db;
9:     /* char pointer ptr_ch */
10:    printf("Current position of ptr_ch: 0x%p\n", ptr_ch);
11:    printf("The position after ptr_ch + 1: 0x%p\n", ptr_ch + 1);
12:    printf("The position after ptr_ch + 2: 0x%p\n", ptr_ch + 2);
13:    printf("The position after ptr_ch - 1: 0x%p\n", ptr_ch - 1);
14:    printf("The position after ptr_ch - 2: 0x%p\n", ptr_ch - 2);
15:    /* int pointer ptr_int */
16:    printf("Current position of ptr_int: 0x%p\n", ptr_int);
17:    printf("The position after ptr_int + 1: 0x%p\n", ptr_int + 1);
18:    printf("The position after ptr_int + 2: 0x%p\n", ptr_int + 2);
19:    printf("The position after ptr_int - 1: 0x%p\n", ptr_int - 1);
20:    printf("The position after ptr_int - 2: 0x%p\n", ptr_int - 2);
21:    /* double pointer ptr_ch */
22:    printf("Current position of ptr_db: 0x%p\n", ptr_db);
23:    printf("The position after ptr_db + 1: 0x%p\n", ptr_db + 1);
24:    printf("The position after ptr_db + 2: 0x%p\n", ptr_db + 2);
25:    printf("The position after ptr_db - 1: 0x%p\n", ptr_db - 1);
26:    printf("The position after ptr_db - 2: 0x%p\n", ptr_db - 2);
27:
28:    return 0;
29: }


The following output is obtained by running the executable, 16L01.exe, of the program in Listing 16.1 on my machine. You might get a different address on your computer, but the offsets should remain the same:

OUTPUT

C:\app>16L01
Current position of ptr_ch: 0x000B
The position after ptr_ch + 1: 0x000C
The position after ptr_ch + 2: 0x000D
The position after ptr_ch - 1: 0x000A
The position after ptr_ch - 2: 0x0009
Current position of ptr_int: 0x028B
The position after ptr_int + 1: 0x028D
The position after ptr_int + 2: 0x028F
The position after ptr_int - 1: 0x0289
The position after ptr_int - 2: 0x0287
Current position of ptr_db: 0x0128
The position after ptr_db + 1: 0x0130
The position after ptr_db + 2: 0x0138
The position after ptr_db - 1: 0x0120
The position after ptr_db - 2: 0x0118
C:\app>

ANALYSIS

As you can see in Listing 16.1, there are three types of pointers—ptr_ch, ptr_int, and ptr_db—declared in lines 6_8. Among them, ptr_ch is a pointer to a character, ptr_int is a pointer to an integer, and ptr_db is a pointer to a double.

The statement in line 10 shows the memory address, 0x000B, contained by the char pointer variable ptr_ch. Lines 11 and 12 display the two addresses, 0x000C and 0x000D, when ptr_ch is added to 1 and 2, respectively. Similarly, lines 13 and 14 give 0x000A and 0x0009 when ptr_ch is moved down to lower memory addresses. Because the size of char is 1 byte, ptr_ch+1 means to move to the memory location that is 1 byte higher than the current memory location, 0x000B, referenced by the pointer ptr_ch.

Line 16 shows the memory location referenced by the int pointer variable ptr_int at 0x028B. Because the size of int is 2 bytes long, the ptr_int+1 expression simply means to move to the memory location that is 2 bytes higher than the current one pointed to by ptr_int. That's exactly what has been printed out in line 17. Likewise, line 18 shows that ptr_int+2 causes the reference to be moved to 0x028F, which is 4 bytes higher than 0x028B. The memory location of 0x0289 is referenced by the ptr_int-1 expression in line 19; 0x0287 is referenced by ptr_int-2 in line 20.

The size of the double data type is 8 bytes long. Therefore, the ptr_db+1 expression is interpreted as the memory address referenced by ptr_db plus 8 bytes—that is, 0x0128+8, which gives 0x0130 in hex format. (See the output made by the statement in line 23.)

Lines 24_26 print out the memory addresses referenced by ptr_db+2, ptr_db-1, and ptr_db-2, respectively, which prove that the compiler has taken the scalar size of double in the pointer arithmetic.

WARNING

    Pointers are useful if you use them properly. On the other hand, a pointer can get you into trouble if it contains a wrong value. A common error, for instance, is to assign a right value to a pointer that actually expects a left one. Fortunately, many C compilers can find such an error and issue a warning message.
    There is another common error that the compiler does not pick up for you: using uninitialized pointers. For example, the following code has a potential problem:

    int x, ptr_int;
    x = 8;
    *ptr_int = x;



    The problem is that the ptr_int pointer is not initialized; it points to some unknown memory location. Therefore, assigning a value, like 8 in this case, to an unknown memory location is dangerous. It may overwrite some important data that is already saved at the memory location, thus causing a serious problem. The solution is to make sure that a pointer is pointing at a legal and valid memory location before it is used.
    You can rewrite this C code to avoid the potential problem like this:

    int x, ptr_int;
    x = 8;
    ptr_int = &x;   /* initialize the pointer */


Pointer Subtraction

For two pointers of the same type, you can subtract one pointer value from the other. For instance, given two char pointer variables, ptr_str1 and ptr_str2, you can calculate the offset between the two memory locations pointed to by the two pointers like this:

ptr_str2 - ptr_str1

However, it's illegal in C to subtract one pointer value from another if they do not share the same data type.

Listing 16.2 gives an example of performing subtraction on an int pointer variable.
 

TYPE
Listing 16.2. Performing subtraction on pointers.


1:  /* 16L02.c: Pointer subtraction */
2:  #include <stdio.h>
3:
4:  main()
5:  {
6:     int *ptr_int1, *ptr_int2;
7:
8:     printf("The position of ptr_int1: 0x%p\n", ptr_int1);
9:     ptr_int2 = ptr_int1 + 5;
10:    printf("The position of ptr_int2 = ptr_int1 + 5: 0x%p\n", ptr_int2);
11:    printf("The subtraction of ptr_int2 - ptr_int1: %d\n", ptr_int2 -
               Âptr_int1);
12:    ptr_int2 = ptr_int1 - 5;
13:    printf("The position of ptr_int2 = ptr_int1 - 5: 0x%p\n", ptr_int2);
14:    printf("The subtraction of ptr_int2 - ptr_int1: %d\n", ptr_int2 -
       Âptr_int1);
15:
16:    return 0;
17: }


After running the executable (16L02.exe) of the program in Listing 16.2 on my machine, I have the following output shown on the screen:

OUTPUT

C:\app>16L02
The position of ptr_int1: 0x0128
The position of ptr_int2 = ptr_int1 + 5: 0x0132
The subtraction of ptr_int2 - ptr_int1: 5
The position of ptr_int2 = ptr_int1 - 5: 0x011E
The subtraction of ptr_int2 - ptr_int1: -5
C:\app>

ANALYSIS

The program in Listing 16.2 declares two int pointer variables, ptr_int1 and ptr_int2, in line 6. The statement in line 8 prints out the memory position held by ptr_int1. Line 9 assigns the memory address referenced by ptr_int1+5 to ptr_int2. Then, the content of ptr_int2 is printed out in line 10.

The statement in line 11 shows the difference between the two int pointers—that is, the subtraction of ptr_int2 and ptr_int1. The result is 5.

Line 12 then assigns another memory address, referenced by the ptr_int1-5 expression, to the ptr_int2 pointer. Now, ptr_int2 points to a memory location that is 10 bytes lower than the memory location pointed to by ptr_int1 (see the output made by line 13.) The difference between ptr_int2 and ptr_int1 is obtained by the subtraction of the two pointers, which is -5 as printed out by the statement in line 14.

 

Pointers and Arrays

As indicated in previous lessons, pointers and arrays have a close relationship. You can access an array through a pointer that contains the start address of the array. The following subsection introduces how to access array elements through pointers.

 

Accessing Arrays via Pointers
Because an array name that is not followed by a subscript is interpreted as a pointer to the first element of the array, you can assign the start address of the array to a pointer of the same data type; then you can access any element in the array by adding a proper integer to the pointer. The value of the integer is the same as the subscript value of the element that you want to access.

In other words, given an array, array, and a pointer, ptr_array, if array and ptr_array are of the same data type, and ptr_array contains the start address of the array, that is

ptr_array = array;

then the expression array[n] is equivalent to the expression

*(ptr_array + n)

Here n is a subscript number in the array.

Listing 16.3 demonstrates how to access arrays and change values of array elements by using pointers.

TYPE
Listing 16.3. Accessing arrays by using pointers.


1:  /* 16L03.c: Accessing arrays via pointers */
2:  #include <stdio.h>
3:
4:  main()
5:  {
6:     char str[] = "It's a string!";
7:     char *ptr_str;
8:     int list[] = {1, 2, 3, 4, 5};
9:     int *ptr_int;
10:
11:    /* access char array */
12:    ptr_str = str;
13:    printf("Before the change, str contains: %s\n", str);
14:    printf("Before the change, str[5] contains: %c\n", str[5]);
15:    *(ptr_str + 5) = `A';
16:    printf("After the change, str[5] contains: %c\n", str[5]);
17:    printf("After the change, str contains: %s\n", str);
18:    /* access int array */
19:    ptr_int = list;
20:    printf("Before the change, list[2] contains: %d\n", list[2]);
21:    *(ptr_int + 2) = -3;
22:    printf("After the change, list[2] contains: %d\n", list[2]);
23:
24:    return 0;
25: }


The following output is displayed on the screen after the executable, 16L03.exe, is created and run from a DOS prompt:

OUTPUT

C:\app>16L03
Before the change, str contains: It's a string!
Before the change, str[5] contains: a
After the change, str[5] contains: A
After the change, str contains: It's A string!
Before the change, list[2] contains: 3
After the change, list[2] contains: -3
C:\app>

ANALYSIS

The purpose of the program in Listing 16.3 is to access a char array, str, and an int array, list. In lines 6 and 8, str and list are declared and initialized with a string and a set of integers, respectively. A char pointer, ptr_str, and an int pointer, ptr_int, are declared in lines 7 and 9.

Line 12 assigns the start address of the str array to the ptr_str pointer. The statements in lines 13 and 14 demonstrate the content of the string saved in the str array, as well as the character contained by the str[5] element in the array before any changes are made to str.

The statement in line 15 shows that the character constant, `A', is assigned to the element of the str array pointed to by the expression

*(ptr_str + 5)

To verify that the content of the element in str has been updated, lines 16 and 17 print out the element and the whole string, respectively. The output indicates that `A' has replaced the original character constant, `a'.

The start address of the int array list is assigned to the ptr_int pointer in line 19. Before I do anything with the list[2] element of the list array, I print out its value, which is 3 at this moment (see the output made by line 20). In line 21, the list[2] element is given another value, -3, through the dereferenced pointer, *(ptr_int + 2). The printf() function in line 22 prints the latest value of list[2].

Related Items

মডুলার C প্রোগ্রামিং (Modular C Programming)

কেবল মাত্র একটি ফাংশন দিয়ে কোনো বড়ো জটিল সমস্যা সমাধানের চেষ্টা করা ভাল প্রোগ্রামিংয়ের পদ্ধতি নয়। সঠিক পদ্ধতি হ'ল সমস্যাটিকে কয়েকটি ছোট ছোট এবং সরল টুকরো করে ফেলা যাতে তা আরও বিশদে বোঝা যায় । তারপরে এই ছোট এবং সরল সমস্যাগুলি সমাধান করার জন্য ছোট ছোট ফাংশন ব্লক তৈরি করা এবং পরে সেগুলি নিয়মানুযায়ী সংযোজিত করা ।

Programming Style

Programming Style

In this section, I'd like to briefly highlight some points that will help you write clean programs that can easily be read, understood, and maintained.

Exercises : Answer the following Question

To help solidify your understanding of this hour's lesson, you are encouraged to answer the quiz questions and finish the exercises provided in the Workshop before you move to the next lesson.

Question and Answer

    Q Is the C preprocessor part of the C compiler?

    A No. The C preprocessor is not part of the C compiler. With its own line-oriented grammar and syntax, the C preprocessor runs before the compiler in order to handle named constants, macros, and inclusion of files.

Compiling Your Code Under Conditions

Compiling Your Code Under Conditions

You can select portions of your C program that you want to compile by using a set of preprocessor directives. This is useful, especially when you're testing a piece of new code or debugging a portion of code.