C Tutorial (25) : Array & Pointers

An array is a special kind of pointer. Because of their similarities, you can use pointer notation to get to array values, and you can use array notation to get to pointed-at values.

Perhaps the most important reason to learn how arrays and pointers overlap is for character string handling. By combining pointer notation (using the dereferencing operation) and array notation (using subscripts), you can store lists of character strings and reference them as easily as you reference array values of other data types.

Also, after you master the heap—a special place in memory that the next tutorial introduces you to— you’ll see that pointers are the only way to get to heap memory, where you put data values.

Array Names Are Pointers

An array name is nothing more than a pointer to the first element in that array. The array name is not exactly a pointer variable, though. Array names are known as pointer constants. The following statement defines an integer array and initializes it:

int vals[5] = {10, 20, 30, 40, 50};

You can reference the array by subscript notation. However, C does more than just attach subscripts to the values in memory. C sets up a pointer to the array and names that point to vals. You can never change the contents of vals; it is like a fixed pointer variable whose address C locks in.

c_array_pointer

                        The array name is a pointer to the first value in the array.

Because the array name is a pointer (that can’t be changed), you can print the first value in the array like this:

printf("The first value is %d.\n", vals[0]);

But more important for this tutorial, you can print the first array value like this, too:

printf("The first value is %d.\n", *vals);

This is also equivalent and accesses vals[0]:

printf("The first value is %d.\n", *(vals+0));

Note : The fact that an array is a fixed constant pointer is why you can’t put just an array name on the left side of an equals sign. You can’t change a constant. (Remember, though, that C relaxes this rule only when you first define the array because C has yet to fix the array at a specific address.)

Because an array name is nothing more than a pointer to the first value in the array, if you want the second value, you only have to add 1 to the array name and dereference that location. This set of printf() lines

printf("The first array value is %d.\n", vals[0]); 
printf("The second array value is %d.\n", vals[1]); 
printf("The third array value is %d.\n", vals[2]); 
printf("The fourth array value is %d.\n", vals[3]); 
printf("The fifth array value is %d.\n", vals[4]);

does exactly the same as this set:

printf("The first array value is %d.\n", *(vals + 0)); 
printf("The second array value is %d.\n", *(vals +1)); 
printf("The third array value is %d.\n", *(vals + 2)); 
printf("The fourth array value is %d.\n", *(vals + 3)); 
printf("The fifth array value is %d.\n", *(vals + 4));

If vals is a pointer constant (and it is), and the pointer constant holds a number that is the address to the array’s first element, adding 1 or 2 (or whatever) to vals before dereferencing vals adds 1 or 2 to the address vals points to.

As you might remember, integers usually take more than 1 byte of memory storage. The preceding printf() statements appear to add 1 to the address inside vals to get to the next dereferenced memory location, but C helps you out here. C adds one int size when you add 1 to an int pointer (and one double size when you add 1 to a double pointer, and so on). The expression *(vals + 2) tells C that you want the third integer in the list that vals points to.

Characters and Pointers

The following two statements set up almost the same thing in memory. The only difference is that, in the second statement, pName is a pointer variable, not a pointer constant:

char name[] = "Anukul"; /* name points to A */ 
char * pName = "Anukul"; /* pName points to A */

Because pName is a pointer variable, you can put it on the left side of an equals sign! Therefore, you don’t always have to use strcpy() if you want to assign a character pointer a new string value. The character pointer will only point to the first character in the string. However, %s and all the string functions work with character pointers just as easily as with character arrays (the two are the same thing) because these functions know to stop at the null zero.

To put a different name in the name array, you have to use strcpy() or assign the string one character at a time—but to make pName point to a different name, you get to do this:

pName = "Anukul Verma";

Tip : The only reason string assignment works is that C puts all your program’s string literals into memory somewhere and then replaces them in your program with their addresses. C is not really putting “Anukul Verma” into pName because pName can hold only addresses. C is putting the address of “Anukul Verma” into pName.

c_char_pointer_1

You now have a way to assign strings new values without using strcpy().

Be Careful with Lengths

The new strings that you assign with ‘=’ can be shorter or longer than the previous strings.

You must be extremely careful, however, not to let the program store strings longer than the first string you point to with the character pointer. Never set up a character pointer variable like this:

main()
{

char * name = "Verma";

/* Rest of program follows... */

and then later let the user enter a new string with gets() like this:

gets(name);     /* Not very safe */

The problem with this statement is that the user might enter a string longer than “Verma”, the first string assigned to the character pointer. Although a character pointer can point to strings of any length, the gets() function, along with scanf(), strcpy(), and strcat(), doesn’t know that it’s being sent a character pointer. Because these functions might be sent a character array that can’t change location, they map the newly created string directly over the location of the string in name. If a string longer than name is entered, other data areas could be overwritten.

If you want to have the advantage of a character pointer—that is, if you want to be able to assign string literals to the pointer and still have the safety of arrays so you can use the character pointer to get user input—you can do so with a little trick.

If you want to store user input in a string pointed to by a pointer, first reserve enough storage for that input string. The easiest way to do this is to reserve a character array and then assign a character pointer to the beginning element of that array:

char input[81]; // Holds a string as long as 80 characters
char *iptr = input; // Also could have done char *iptr = &input[0]

Now you can input a string by using the pointer as long as the string entered by the user is not longer than 81 bytes long:

gets(iptr); /* Makes sure that iptr points to the string typed by the user */

You can use a nice string-input function to ensure that entered strings don’t get longer than 81 characters, including the null zero. Use fgets() if you want to limit the number of characters accepted from the user. fgets() works like gets(), except that you specify a length argument. The following statement shows fgets() in action:

fgets(iptr, 81, stdin);   /*Gets up to 80 chars and adds null zero */

The second value is the maximum number of characters you want to save from the user’s input. Always leave one for the string’s null zero. The pointer iptr can point to a string as long as 81 characters. If the user enters a string less than 81 characters, iptr points to that string with no problem. However, if the user goes wild and enters a string 200 characters long, iptr points only to the first 80, followed by a null zero at the 81st position that fgets() added, and the rest of the user’s input is ignored.

Tip : You can use fgets() to read strings from data files. The third value of fgets() can be a disk file pointer, but you’ll learn about disk pointers in later tutorials. For now, use stdin as the third value you send to fgets() so that fgets() goes to the keyboard for input and not somewhere else.

You also can assign the pointer string literals using the assignment like this:

iptr = "Anukul Verma";

Arrays of Pointers

If you want to use a bunch of pointers, create an array of them. An array of pointers is just as easy to define as an array of any other kind of data, except that you must include the * operator after the data type name.

int * ipara[25]; /* 25 pointers to integers */ 
char * cpara[25]; /* 25 pointers to characters */

The array of characters is most interesting because you can store a list of strings in the array. More accurately, you can point to various strings. The following program illustrates two things: how to initialize an array of strings at definition time and how to print them using a for loop:

/* This program declares and initializes an array of character
   pointers and then asks for ratings associated */

#include <stdio.h>

main()
{
int i;
int ctr = 0; 
char ans;

//Declaring our array of 9 characters and then initializing them 
char * movies[9] = {"Amour", "Argo",
                    "Beasts of the Southern Wild", 
                    "Django Unchained",
                    "Les Miserables",
                    "Life of Pi", "Lincoln",
                    "Silver Linings Playbook", "Zero Dark Thirty"};

int movieratings[9]; // A corresponding array of 9 integers for movie ratings

char * tempmovie = "This will be used to sort rated movies"; 
int outer, inner, didSwap, temprating; // for the sort loop

printf("\n\n*** Oscar Season 2012 is here! ***\n\n");
printf("Time to rate this year's best picture nominees:");

for (i=0; i< 9; i++)
{
printf("\nDid you see %s? (Y/N): ", movies[i]); 
scanf(" %c", &ans);

if ((toupper(ans)) == 'Y')
{
printf("\nWhat was your rating on a scale "); 
printf("of 1-10: ");
scanf(" %d", &movieratings[i]);

ctr++; // This will be used to print only movies you've seen
continue;
}
else
{
movieratings[i] = -1;
}
}

// Now sort the movies by rating (the unseen will go to the bottom)
for (outer = 0; outer < 8; outer++)
{
didSwap = 0;
for (inner = outer; inner < 9; inner++) { if (movieratings[inner] > movieratings[outer])
{
tempmovie = movies[inner]; 
temprating = movieratings[inner]; 
movies[inner] = movies[outer];

movieratings[inner] = movieratings[outer]; 
movies[outer] = tempmovie; 
movieratings[outer] = temprating;

didSwap = 1;
}
}
if (didSwap == 0)
{
break;
}
}

// Now to print the movies you saw in order 
printf("\n\n** Your Movie Ratings for the 2012 Oscar "); 
printf("Contenders **\n");

for (i=0; i < ctr; i++)
{
printf("%s rated a %d!\n", movies[i], movieratings[i]);
}

return(0);
}

Figure shows how the program sets up the movies array in memory. Each element is nothing more than a character pointer that contains the address of a different person’s name. It’s important to understand that movies does not hold strings—just pointers to strings.

c_char_pointer_2

The movies array contains pointers to strings.

See, even though there is no such thing as a string array in C (because there are no string variables), storing character pointers in movies makes the program act as though movies is a string array.

Use character pointers if you want to assign string literals directly.

Pointer < Prev                                                                                         Next > Heap Memory

Advertisements

2 comments

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s