Suppose you wanted to write a program to read in, say, five numbers, and print them out in the opposite order in which they were entered. How would you do it? Probably something like this:
#include <stdio.h>
main()
{
int n1, n2, n3, n4, n5;
printf ("Please enter five integers\n");
scanf ("%d", n1);
scanf ("%d", n2);
scanf ("%d", n3);
scanf ("%d", n4);
scanf ("%d", n5);
printf ("%d\n", n5);
printf ("%d\n", n4);
printf ("%d\n", n3);
printf ("%d\n", n2);
printf ("%d\n", n1);
}
Sure, this would work. But what if you wanted not five, but ten, or fifty numbers? The code will keep getting longer and longer. Obviously, this is not the answer.
The answer is to use arrays. What an array basically does, is to create several variables of the same type. You can access any of these variables by specifying and index, which is basically and integer. What makes this useful, is the fact that this index can be a variable, or in fact, any expression that produces an integer result.
Now that you know what an array is, here's how you declare one:
<type> <identifier> [<num_elements>]; Here, type can be any type... int, float, long etc. The identifier is the name given to the array, and follows the same naming rules as any other variable. Finally, num_elements is the number of elements (explained shortly) in the array. Remember, that in the declaration you MUST use a constant to specify the number of elements, because the compiler must know how many elements to allocate at compile-time. For example, you can use 10 or 123 * 4 + 5 but you can't use x or 5 * x. However, you can use #defined constants. For example,
int i[5];
declares an array of five ints. You can access these ints as: i[0], i[1], i[2], i[3] and i[4]. Each int in the array is known as an element. Accessing an individual element by using square brackets is known as dereferencing or indirection. The number used for dereferencing is called the index or subscript. As mentioned earlier, the index can be not only an integer constant, but any integer expression. Indeed, this is what makes arrays useful, and different from a bunch of variables.
In a moment we'll rewrite the above program using an array. But first make note of one a couple of important points.
C is one of the few languages that won't perform such checks. This is a part of the language's philosophy that says "the programmer knows what he/she is doing." While it may make life a little difficult for the new programmer, it is one of the reasons why C programs run faster than practically every other high level language.
Now, let's rewrite the program presented at the beginning of this chapter, using arrays.
#include <stdio.h>
main()
{
int n[5], i; /* An array of five integers (n) and an integer variable (i) */
printf ("Please enter five integers\n");
for (i = 0; i < 5; i++) /* Note: i=0 and i < 5 (not <=) */
scanf ("%d", &n[i]);
for (i = 4; i >= 0; i++)
printf ("%d\n", n[i]);
}
This program, obviously is a lot easier to code and more flexible and extensible than the first program.
So far you've used string constants (also called string literals) in printf() and scanf() statements. To refresh your memory, a string is a collection of characters... in other words and array of characters! That's it! You know about characters (chars) and you know about arrays, so now, you know about strings! Okay, thats a bit of an exaggeration, because there are still a few things to be discussed.
In C, strings are stored in ASCIIZ format. Its also called a NULL-terminated string. (Recall that '\0' or ASCII zero is called the NULL character.) In this format, any number of characters form a string. The string is terminated by a NULL character. The advantage of this system is that there is no theoretical limit on the length of the string. The disadvantage is that to find the length of the string, you need to scan the whole string till you find the NULL character.
Now, let's write a program to read in a string, find its length and then echo the string. This will give you a basic understanding of how to manipulate strings.
#include "stdio.h"
main()
{
char str[101];
int len;
scanf ("%100s", str);
len = 0;
while (str[len] != '\0')
len++;
printf ("Length of string:%d", len);
printf ("Original string: %s", str);
}
First thing you'll notice is that you use %s to read and write strings using scanf and printf respectively. With scanf, the field width specifies the maximum number of characters that may be put in the string. We've used 100 here, although the array has 101 elements, because scanf appends a NULL ('\0') character to make it a valid ASCIIZ string. Note that the length printed hare does not include the NULL character. Indeed whenever you refer to the length of a string, the NULL character is NOT counted.
With printf, the field width has the same effect as it has with other data types. The precision specifier gives the maximum number of characters that may be printed.
If you've read the code carefully enough, you'll notice that there is no ampersand (&) before str in the string statement, i.e., we've used str, not &str. Well, until we get to the chapter on pointers, it is impossible for me to really explain why the ampersand is there in the first place, so its even harder to explain why we don't use it here. For now, just remember, that you should prefix ALL arguments to scanf with an ampersand EXCEPT for strings.
You'll be surprised to learn that unlike practically every other language, the C language itself does not have inbuilt support for strings. In other words, you can't use the == operator to compare strings, or = to make one string equal to another (except during initialization) etc. Instead, there are a number of standard functions which are used for manipulating strings.
Standard functions are functions like printf which are made available by the compiler, or more correctly the libraries accompanying the compiler. Thus you can in fact rewrite these functions yourself if you ever need to do so, by writing your own functions.
You'll learn more about functions in the next chapter.
These functions are defined in the header file, string.h. This header file is just like the header file stdio.h that you include in practically all programs. So if you want to use the string functions in a program, you should use the line #include <string.h> at the beginning of the program.
This table lists the commonly used string functions. These functions are all you will need at the present. I'll present a more complete list later on, in the appendix. If you want to, you can also refer to your compiler's documentation, or any book on C you may have.
Note that char s[] means "string s with any number of elements"
Function | Argument(s) | Return | Comments |
---|---|---|---|
strcpy | char d[], char s[] | char[] | Copy s to d, including the NULL. Return d. |
strncpy | char d[], char s[], int n | char[] | Copy maximum n character from s to d. Pad with NULL if s has less than n characters. Return d. |
strcat | char d[], char s[] | char[] | Attach string s at the end of string d (concatenate). Return d. |
strncat | char d[], char s[], int n | char[] | Concatenate maximum n characters from s to d. Append NULL if necessary. Return d. |
strcmp | char s1[], char s2[] | int | Compare string s1 to string s2. Return < 0 if s1 < s2, 0 if s1 = s2 and > 0 if s1 > s2 |
strncmp | char s1[], char s2[], int n | int | Same as above, but compare maximum n characters |
strlen | char s[] | int | Return the length of the string |
When you use the copy and concatenate functions, it is very important the ensure that the destination string is big enough to hold the result. The following program will make the use of the string functions clear. You will see that the functions are all intuitive and easy to use.
#include <stdio.h>
#include <string.h>
main()
{
char str1[40] = "Programming in C is ";
char str2[] = "fun.";
char str3[] = "interesting.";
printf ("str1 is: %s\n", str1);
printf ("Length of Str1 = %d\n", strlen (str1));
printf ("After concatenation: %s\n", strcat (str1, str2));
printf ("After another concatenation: %s\n", strcat (str1, str3));
printf ("Length of str1, now: %d\n", strlen (str1));
}
Before we discuss the output of the program, look at the way the strings are initialized. I said a little earlier that you can't use = to make one string equal to another. This rule does not apply to initialization. You can use = to assign the initial value of a string, in the declaration.
Then look at the next two initializations. We've not specified any size for the arrays! So what size does the compiler use? It uses the minimum size required to hold the string. In this case, five and thirteen respectively (the number of characters plus one for the terminating NULL). You can omit the array size, ONLY if you initialize the string in the declaration, not if, for example you read it in from the keyboard using scanf. However, I've specified the number of elements in the first declaration (str1) because we need to leave space for the concatenations, later on.
Now, for the output of the program itself. There really is nothing complicated about it. But just take a little glance at the second-last line. If you were expecting something like,
"Programming in C is interesting."
you would be wrong. We've already concatenated the string "fun.", so what you actually end up with is:
"Programming in C is fun.interesting."
I've just done this to highlight the fact that functions like strcat and strcpy modify the first string passed to it, str1 in this case. The functions strlen and strcmp, of course don't modify the strings.
There are a set of functions related to classifying and converting characters. For example, checking whether a character is an alphabet, whether it is a digit etc., or for converting to upper or lower case, etc. Although these functions are not directly related to strings, they are often used in conjunction with them. To use these functions, you must include the ctype.h header file, with:
#include <ctype.h>
All functions except the first two return an integer - zero for false, non-zero for true. The first to functions return char. Looking at the functions and their uses makes this fact quite logical and obvious. All functions accept a char argument.
Function | Purpose |
---|---|
tolower | Convert character to lower-case. Return the chracter unchanged if it is not an upper-case character. |
toupper | Convert character to UPPER-CASE. Return the character unchanged if it is not a lower-case character. |
isalpha | Is the character an alphabet? |
isdigit | Is it a digit? |
isxdigit | Is it a hexadecimal digit? |
isalnum | Is it alphanumeric? (digit or alphabet) |
islower | Is it a lower-case letter? |
isupper | Is it an upper-case letter? |
isgraph | Is it a printing character, except space? |
isprint | Is it a printing character, including space? |
ispunct | Is it a punctuation character? (comma, semicolon etc.) |
isspace | Is it a white space character? |
iscntrl | Is it a control character? |
We'll now write a couple of example programs to illustrate the use of thses functions. The first program uses the classification functions (is...)
#include <stdio.h>
#include <ctype.h>
main()
{
char c;
printf ("Enter a character and press enter: ");
c = getchar();
printf ("\nCharacter is: ");
if (isprint (c))
printf ("Printable ");
if (isgraph (c))
printf ("Non-white-space ");
if (isspace (c))
printf ("White-space ");
if (isupper (c))
printf ("Upper-case ");
if (islower (c))
printf ("Lower-case ");
if (isalpha (c))
printf ("Alphabet ");
if (isdigit (c))
printf ("Digit ");
if (iscntrl (c))
printf ("Control-Character ");
printf ("\n");
}
Just run the program and type in a character, and press enter. It will give you the classification of that character. For example, if you type in a, it will come up with:
Character is: Printable Non-white-space Lower-case Alphabet
Now, lets write a program to accept a string from the user and convert it to upper-case.
#include <stdio.h>
#include <ctype.h>
#include <string.h>
main()
{
char str[100];
int i;
printf ("Enter a string: ");
scanf ("%100s", str);
for (i = 0; i < strlen (str); i++)
str[i] = toupper (str[i]);
printf ("\nHere's the string in all UPPER-CASE:\n");
printf ("%s\n", str);
}
This program primarily shows you how to use the toupper function. It also shows you a little bit about manipulating strings.
Arrays are an important way of storing and processing data. We're about to get started on multi-dimensional arrays. Its going to be virtually impossible to grasp anything else in this chapter, unless you first understand one-dimensional arrays. Re-read the chapter if you need to, and write your own programs to eliminate any lingering doubt.
When I introduced arrays at the beginning of this chapter, I mentioned that you can have an array of any type. That type can itself be an array. So you have an array of arrays. In other words a two-dimensional array.
This section will focus on understanding the concept of multi-dimensional arrays. If you've programmed in almost any other language, you'll probably be familiar with arrays. If so, you can skim through this section. Just carefully learn the parts dealing with the C syntax.
Alternatively, you could visualise a two-dimensional array to be, say, a table or spreadsheet, and each element is dereferenced using two coordinates, say row and column. However, the first visualisation (array of arrays) has certain advantages you'll see in a later chapter, and is easier to expand to three and more dimensional arrays. So although it may be a little harder to grasp at first, I'll stick with the array-of-arrays model.
You declare a two-dimensional array as follows:
<type> <identified>[i][j]
Where i and j are the two indices. You can visualize this as a one-dimensional array of i arrays. Each element of this array of arrays is an array of j integers. Confused? Read that sentence carefully again. Look at this code fragment:
int array[i][j]; /* and ixj array */
array[n] /* this is an array of int having j elements */
(array[n])[m] /* this is the m-th integer in the n-th such array */
array[n][m] /* this is equivalent to the above statement */
So you see that we dereference the array twice. The first dereferencing (indirection) gives an array with as many elements as the second index in the declaration (j in this case). The second indirection gives a variable of the declared type.
As a simple example of this, lets write a program that reads in a 3x3 matrix of (nine) integers and finds the sum of rows, columns and diagonals.
#include <stdio.h>
main()
{
int matrix[3][3], i, j, sum;
for (j = 0; j < 3; j++)
for (i = 0; i < 3; i++)
{
printf ("Enter element (%d, %d):", i, j);
scanf ("%d", &matrix[i][j]);
}
for (j = 0; j < 3; j++)
{
sum = 0;
for (i = 0; i < 3; i++)
sum += matrix[i][j];
printf ("Sum of row %d = %d\n", j, sum);
}
for (i = 0; i < 3; i++)
{
sum = 0;
for (j = 0; j < 3; j++)
sum += matrix[i][j];
printf ("Sum of column %d = %d\n", j, sum);
}
sum = 0;
for (i = 0; i < 3; i++)
sum += matrix[i][i];
printf ("Sum of first diagonal = %d", sum);
sum = 0;
for (i = 0; i < 3; i++)
sum += matrix[i][2 - i];
printf ("Sum of second diagonal = %d", sum);
}
An important special case of a two-dimensional array is of char. In other words, an array of strings. When you declare the array remember that the first subscript specifies the number of strings in the array. The second subscript specifies the maximum length of each string (including the terminating NULL, of course). For example,
char Name[10][41];
This declares an array of ten strings. Each string can hold upto forty characters plus the terminating NULL. The first string is Name[0], the second on is Name[1] etc. Name[3][15] is the sixteenth character in the fourth string.
I won't present an example program on arrays of strings. You, however should write a program or to and make sure everything works the way you understand it.
You can use a third subscript to the declaration to create a three dimensional array. Once again you can visualize this in to ways. As the three coordinates of a cubic or cuboidic system. Or as an array of two dimensional arrays. I won't lay much emphasis on three dimensional arrays because they are very raraly used excepe, perhaps in scientific or enginering applications. EIther way, they probably won't be of much use in the immediate future. However, if you're interested you can try out a few programs that use three-dimenstional arrays.
There's no reason to stop at three. You can have as many dimensions as you like. Just keep adding subscripts. You can visualize a four-dimensional array as an array of three dimensional arrays or if you're real whacky and/or a mathematical whiz of some sort, as the four coordinates of a tesseract. But honestly, if you find that you need a four dimensional array, there's probably something wrong either with your understanding of arrays or with the way you've modelled your data.
You can initialize a array by specifying the data items to be put in array, in braces, separated by commas. For multi-dimensional arrays, use braces within braces (remember the array-of-arrays visualisation!). For example,
char Vowels[6] = { 'a', 'e', 'i', 'o', 'u', '\0' };
char Vowels2[6] = "aeiou"; /* Exactly equivalent */
int Prime[5] = { 2, 3, 5, 7, 11 };
float Numbers[2][5] = { {2, 4, 6, 8, 10}, {3, 6, 9, 12, 15} };
char Words[3][15] = {"Vinay's", "C", "Tutorial"};
int Cube[2][2][2] = { { {1, 2}, {3, 4] }, { {5, 6}, {7, 8} } };
You'll perhaps need a little explanation of the last few examples. The Numbers example is the declaration of a simple 2x5 array of int. Here, Numbers[0, 0] is 2, Numbers[1, 2] is 9 and so on.
The Words example is a 3x15 array of char. In other words, it is an array of three strings. Each string is 15 chars long. i.e. 14 string characters plus the terminating NULL. Now, notice that none of the strings used in the initialization are 14 characters long. That's perfectly fine. If you specify too few initializers, the compiler just leaves the rest uninitialized. Specifying too many initializers, on the other hand, of course, is an error. This applies equally to all types of arrays. For example, you could declare an array of 10 ints and initialize only the first 5.
The last example (Cube) might just confuse you as to which element gets initialized to what value. Remember that the outermost brace controls the first (leftmost) subscript, the next brace controls the next subscript and so on. So, for example, the value 4 is assigned to Cube[0][1][1], and 6 is assigned to Cube[1][0][1].
When you declare an array with initialization, you can omit the value of the first subscript. The compiler will cound the number of initializers and make the array of the correct size. Note that for a multi-dimentional array you can skip only the first subscript. For example,
int Random[] = { 3, 7, 2, 10, 4, 1, 13 };
int Random[7] = { 3, 7, 2, 10, 4, 1, 13 }; /* Exactly equivalent */
char Words[][15] = { "Vinay's", "World", "of", "Programming" );
/* Assigned size: [4][15] */
Finally, an important thing to note is that you can assign values to the whole array like this, only during initialization. For example, the following is ILLEGAL:
main()
{
int arr[5];
printf ("Welcome!\n");
arr = { 2, 4, 6, 8, 10 }; /* ILLEGAL */
}
To assign values to the array later on (after the declaration, during execution) you should either use a loop, or use separate assignments for each element. Directly using = might be particularly tempting when you're using strings. Remember, in C, strings are not a basic data type. A string is only an array of characters. The correct way to copy strings, of course, is usually to strcpy().
C Tutorial Quicklist | Start Page
| Contents |
| Ch 1:Introduction
| Ch 2:Variables
| Ch 3:Loops
| Ch 4:Expresisons
| Ch 5:Control Flow |
| Ch 6:Basic I/O
| Ch 7:Arrays |
| Home Page
| Beginners
| Advanced
| Links
| Downloads
| Books |
| C Tutorial
| HYPERSearch
| Guestbook
| Feedback |