5. Arrays – C Programming Essentials

Chapter 5. Arrays

This chapter describes the representation of linear data structures by using one of the methods of sequential allocation of storage. Other methods of storage allocation will be discussed in the chapter on Pointers and Functions. Although the linear method of allocation is suitable for certain applications, there are many applications where this is found unacceptable. However, this method is widely used and helps in development of simpler programs. Initially, we focus on arrays in C, both single-dimensional and multi-dimensional, and then proceed to the different initialization techniques. Operations on matrices are introduced. Two types of sequential representations of multi-dimensional arrays are introduced. A string is not a separate data type in C. However, single-dimensional character arrays can be used as strings. Two-dimensional character arrays are discussed as arrays of strings. We then move on to some common string manipulation functions available within the standard C library.

Need for Arrays

The need for arrays comes from the problem of representing large amounts of data in the program. It might often be required to store a number of items, say, for processing. The items may be all related in the fact that they all represent a particular theme. However, to declare n variables for storage of the n items is tedious and a non-elegant approach. As programmers, we represent such sequences by arrays.

In C, an array is an aggregate variable consisting of a collection of the same type. The area identifier is the name given to the entire collection; individual items are identified in terms of their location in the array. The elements in an array are stored in consecutive memory locations. Arrays in C can be broadly classified into two groups — single or one-dimensional and multi-dimensional. We will discuss these in detail in the subsequent subsections.

Single-Dimensional Arrays

To define an array containing variables of a particular type, it is required to specify the type (as in other variable definitions), give the array an identifier (its name), and indicate the array’s size within subscripts. The general form of an array definition is then:

datatype array_identifier[size];

The data_type in the above definition indicates the type of elements that can be contained in the array. Data_types can be any of the basic or user-defined data_types.

The size field specifies the maximum number of elements that can be stored in the array. The definition

int a[20];

defines an array, a, of size 20, i.e., a block of 20 consecutive memory locations named a[0], a[1], a[2], ..., a[19]. The allocated array is shown in Figure 5.1.

Figure 5.1. Allocated Integer Array of 20 Elements

The notation a[i] (same as a[i] in mathematics) refers to the ith element of the array. Since every identifier is associated with a specific memory address, the array elements as well as the array have a memory address to which they are associated. The first array element has the lowest address in the array and also represents the address of the entire array variable (also called the base address of the array). The following elements in the array are addressed by successive increments of the base address by the size of each element in the array. For example, if the array base address is 2000 and the array stores n integers, with 4 bytes for each integer, then the first element has address 2000, the second has address 2004, the third 2008, and so on.

To cite another example for the need of arrays, let us use an array to store daily temperature readings for a month:

double daily_temperature[31];

This definition creates an array variable named daily_temperature. The data type is double indicating that the daily temperature is expected to be a double-precision floating-point number. The size of 31 within subscripts indicates that the array is to contain 31 elements, representing daily temperature values for the 31 days of the month.

To access a particular element in the array, the array identifier is followed by an integer value in subscripts corresponding to the element’s position in the array. In C, array subscripts range from 0 to n-1 for an array of size n. So, to refer to the 10th element, say, of the daily_temperature array, we write daily_temperature[9].

To illustrate some of the basics of arrays, let us move on to our first program using arrays. It fills an array with values, prints the contents of the array and finds out the sum of the elements of the array.

Example . 

Program Ch05n01.c

 /*Program to illustrate the use of 1-D arrays –
 *fill an array, print out values and sum the elements of the array
 */

 #include <stdio.h>

 int main()
 {
    int a[6];    // Space for a[0],a[1], ... , a[5] is allocated
    int i,Sum=0;

    /*Fill the array*/
    for(i=0;i<=5;i++)
        a[i]=i*i;
    /*Print the values in the array*/
    printf("\n");
    for(i=0;i<=5;i++)
        printf("a[%d]=%d.\n",i,a[i]);
    /*Calculate the sum of the values of the elements in the array*/
    for(i=0;i<=5;i++)
        Sum+=a[i];
    printf("The Sum is %d.\n",Sum);
    return 0;
 }

 End Program
EXECUTION

a[0]=0.
a[1]=1.
a[2]=4.
a[3]=9.
a[4]=16.
a[5]=25.
The Sum is 55.

The array, a, used in the above program requires 6 locations to store an int. On a compiler with 4 bytes for an int value, the array requires 6*4=24 bytes of storage. If a[0] is stored at memory location b (the base address of the array a), the remaining array elements are successively stored at locations b+4, b+8, b+12, b+16, and b+20, respectively, for array elements a[1] ... a[5].

It is important to note that the index of an array used in the subscript notation a[i] can also be interpreted as a+i. In C, the array name gives the base address of the array. The index i is added with the base address of the array to give the address of the ith element of the array. But hold on! Didn’t we say that the address of array element a[i-1] has to be incremented by the size of the data type of the elements in the array to obtain the address for a[i]? As such, the address of a[i] should have been written as a+(i*s), where s is the size in bytes of the data type representing an element of a. However, this multiplication is implicit for C and it is enough to write a+i to access the ith element of the array.

There is another important thing to note about C arrays. C does not provide bounds checking on its arrays. We can, therefore, write a[7]=49 in the above example program without the compiler generating an error or a warning message. As a programmer, we must be careful about the array bounds accessed. Array-bounds violations result in incorrect programs and unpredictable results may be obtained.

Single-Dimensional Array Initialization

Arrays may be of auto, extern, static, or const storage class, but cannot be of register storage class. Automatic (auto) is the default storage class for arrays. However, there is a difference in the initialization of arrays unlike other automatic variables. The initial values must be written as a list of values, in the order in which they are to be assigned to the individual array elements. The list is a sequence of constant values separated by commas and enclosed within braces. For example,

static float x[7]={-1.1, 0.2, 33.4, 4.4, 5.05, 0.0, 7.8};

is an initialization that initializes x[0] to -1.1, x[1] to 0.2, and so on.

If we do not initialize an array that has a static declaration, C initializes the array elements to zero by default. The contents of an automatic array, however, are undefined until explicit initialization is performed.

It is not always necessary to specify the number of elements within subscripts. If we define and initialize a static or an extern array, we can leave out the integer within the subscripts specifying the size of the array. The compiler will then automatically allocate the exact number of cells necessary to store the initial number of elements. The following definition will create and initialize a static array count of 5 integers.

static int count[]={4,5,2,19,76};

The allocated array is shown in Figure 5.2.

Figure 5.2. Initialized Static Integer Array of 5 Elements

If fewer initial values are provided in an initialization list than that specified in the size of the array, the initialization will begin with the first index (index 0). After the values in the list have been exhausted, the remaining array elements will be initialized with a numeric zero (0) in case of an integer or a floating-point array, and with a ‘\0’ in case of a character array.

The definition statement generates an error if we supply more initial values than the specified size of the array.

For example,

int c[5]={4,5,2};

will initialize c[0] with 4, c[1] with 5, c[2] with 2, and c[3] and c[4] will be initialized to numeric 0.

The allocated array is shown in Figure 5.3.

Figure 5.3. Partly Initialized Integer Array of 5 Elements

For a character array, the partial initialization,

char p[5]={'t','o'};

will initialize p[0] with ‘t’, p[1] with ‘o’, and p[2], p[3], and p[4] will be initialized to the character constant ‘\0’.

The allocated array is shown in Figure 5.4.

Figure 5.4. Partly Initialized Character Array of 5 Elements

We can initialize the elements of an array with numeric zeroes by the following statement.

int c[5]={0};

The allocated array will be as in Figure 5.5.

Figure 5.5. Array Initialization to Zero

For our final example on single-dimensional array initialization, the definition:

float Rate[5]={4.5, 0.3, 5};

will allocate the floating-point array as shown in Figure 5.6.

Figure 5.6. Partial Floating-Point Array Initialization

In general, it is a good practice to provide explicit array initialization as with any other variable. Initialization is necessary to avoid unintentional computation using default garbage values as in the case of automatic variable.

Multi-Dimensional Arrays

The C language allows any type of arrays, including arrays of arrays. The number of indices used to find a particular element in an array is called the dimension of the array. So far, we discussed only one-dimensional arrays with one index within subscripts. If we use two pairs of subscripts, we obtain a two-dimensional array. This idea can be iterated to form arrays of higher dimensions. Arrays of more than one dimension are called multi-dimensional arrays. The general form of a multi-dimensional array definition is:

data_type array_identifier[d1][d2][d3]...[dn];

The table in Figure 5.7 lists some examples of multi-dimensional array definition.

Table 5.7. Examples of Multi-dimensional Arrays

Array Definition

Remark

int b[3][5];

A Two Dimensional Array

int c[7][9][2];

A Three Dimensional Array

int d[3][3][5][4];

A Four Dimensional Array

A k-dimensional array has a size for each of its k dimensions. Let Si represent the size of its ith dimension; then, the definition of the array will allocate space for S1 × S2 × S3 × ... × Sk elements. Thus, from the above table (Fig. 5.7), the array b has 3 × 5 = 15 elements; array c has 7 × 9 × 2 = 126 elements; and array d has 3 × 3 × 5 × 4 = 180 elements. Starting at the base address of the array, all array elements are stored contiguously in memory. Even though array elements are stored contiguously in memory, let us first visualize a two-dimensional array as a rectangular matrix with rows and columns. For example, if we define a two-dimensional array, mat, as

int mat[3][4];

we can think of the array elements to be arranged as shown in Figure 5.8:

Figure 5.8. Matrix Representation of the 2D Array

To illustrate the above ideas, let us write a program that fills a two-dimensional array, prints out values, and sums up the elements of the array.

Example . 

Program Ch05n02.c

 /*Program to illustrate the use of 2-D arrays –
 *fill an array, print out values and sum the elements of the array
 */

 #include <stdio.h>

 int main()
 {
    const int M=3,N=4;   // M holds no of rows, N holds no of cols
    int a[M][N];         // Space for elements of a is allocated
    int i,j,Sum=0;

    /*Fill the array*/
    for(i=0;i<M;i++)
        for(j=0;j<N;j++)
            a[i][j]=i+j;

    /*Print the values in the array*/
    printf("\n");
    for(i=0;i<M;i++)
    {
        for(j=0;j<N;j++)
            printf("a[%d][%d]=%d  ",i,j,a[i][j]);
        printf("\n");
    }
    /*Calculate the sum of the values of the elements in the array*/
    for(i=0;i<M;i++)
        for(j=0;j<N;j++)
            Sum+=a[i][j];
    printf("The Sum is %d.\n",Sum);
    return 0;
 }

 End Program
EXECUTION

a[0][0]=0  a[0][1]=1  a[0][2]=2  a[0][3]=3
a[1][0]=1  a[1][1]=2  a[1][2]=3  a[1][3]=4
a[2][0]=2  a[2][1]=3  a[2][2]=4  a[2][3]=5
The Sum is 30.

Explanation: The size of the array specified in subscripts must be a constant value; otherwise, the compiler generates an error. Hence, the variables M and N, used to store the number of rows and the number of columns, respectively, must be defined as of data type const. Two nested for loops are used to access the array with the two indices i and j in each case. It is important to note that this nesting can be extended to general multi-dimensional arrays — one for loop in the nesting structure is required for each dimension.

Matrices

Matrices play a vital role in different fields like mathematics, statistics, and engineering. Matrix processing is extensively required in image processing and other fields of computer science. To carry out a matrix operation by hand may be cumbersome, for large matrices it is both time-consuming and prone to errors. Hence, it is customary to write programs to perform the above tasks. As stated earlier, a matrix in mathematics is synonymous with a two-dimensional array in computer science. We move on to highlighting some of the basic and commonly used matrix operations.

Matrix Addition

A matrix in mathematics is written as Amxn (Read: The matrix A with dimensions m cross n), where m is the number of rows in the matrix and n is the number of columns. Two matrices are conformable for addition if they have equal number of rows and columns.

Consider the following two 3×3 matrices C and D.

The addition of the two matrices can be calculated into E3×3 as

Every element of E is equal to the sum of the corresponding elements of the matrices C and D, i.e.,

Ei,j = Ci,j + Di,j

where i and j refer to the ith row and the jth column respectively.

Let us move on to writing the program for matrix addition.

Example . 

Program Ch05n03.c

 /*Program to add two matrices*/

 #include <stdio.h>

 int main()
 {
    const int M=3,N=3;  // M holds no of rows, N holds no of cols
    int c[M][N],d[M][N],e[M][N];
    int i, j;

    /*Read in the values for the c and d matrices*/
    printf("\nEnter the elements of matrices c and d row-wise");
    printf("\nIn the format c[i][j],d[i][j]...\n");
    for(i=0;i<M;i++)
        for(j=0;j<N;j++)
            scanf("%d,%d",&c[i][j],&d[i][j]);

    /*Print the matrix c*/
    printf("\nThe matrix c as entered is...\n");
    for(i=0;i<M;i++)
    {
        for(j=0;j<N;j++)
            printf("%4d",c[i][j]);
        printf("\n");
    }

    /*Print the matrix d*/
    printf("\nThe matrix d as entered is...\n");
    for(i=0;i<M;i++)
    {
        for(j=0;j<N;j++)
            printf("%4d",d[i][j]);
        printf("\n");
    }

    /*Calculate the sum of the two matrices into matrix e*/
    for(i=0;i<M;i++)
        for(j=0;j<N;j++)
           e[i][j]=c[i][j]+d[i][j];

    /*Print the result matrix e*/
    printf("\nThe matrix e as a sum of c and d is...\n");
    for(i=0;i<M;i++)
    {
        for(j=0;j<N;j++)
            printf("%4d",e[i][j]);
        printf("\n");
    }
    return 0;
 }

End Program
EXECUTION (Boldface represents input)

Enter the elements of matrices c and d row-wise
In the format c[i][j],d[i][j]...
 1,4
 2,5
 3,6
 4,7
 5,8
 6,9
 7,1
 8,2
 9,3
The matrix c as entered is...
    1   2   3
    4   5   6
    7   8   9
The matrix d as entered is...
   4   5   6
   7   8   9
   1   2   3
The matrix e as a sum of c and d is...
   5   7   9
  11  13  15
   8  10  12

The readers are expected to understand the program by entering different values in the c and d matrices. It is important to note that the same code is repeated for printing the c, d and e matrices. If a function is used instead, the total length of the program would be reduced and the code would become more elegant as in the following example:

Example . 

Program Ch05n04.c

 /*Program to add two matrices using  functions*/

 #include <stdio.h>

 int main()
 {
       const int M=3,N=3;    // M holds no of rows, N holds no of cols
       int c[M][N],d[M][N],e[M][N];
       int i, j;

       /*Read in the values for the c and d matrices*/
       printf("nEnter the elements of matrices c and d row-wise");
       printf("nIn the format c[i][j],d[i][j]...n");
       for(i=0;i<M;i++)
           for(j=0;j<N;j++)
               scanf("%d,%d",&c[i][j],&d[i][j]);

       /*Print the matrix c*/
       printf("nThe matrix c as entered is...n");
       print_matrix(c);

       /*Print the matrix d*/
       printf("nThe matrix d as entered is...n");
       print_matrix(d);

       /*Calculate the sum of the two matrices into matrix e*/
       for(i=0;i<M;i++)
            for(j=0;j<N;j++)
                     e[i][j]=c[i][j]+d[i][j];

        /*Print the result matrix e*/
        printf("nThe matrix e as a sum of c and d is...n");
              print_matrix(e);
        return 0;
        }

        print_matrix(int x[][])
        {
              int i, j;
              for(i=0;i<M;i++){
                  for(j=0;j<N;j++)
                      printf("%4d",x[i][j]);
                  printf("n");
 }
 End Program

Matrix subtraction is similar to matrix addition in that the addition of the corresponding elements of the two matrices is replaced by the subtraction operation.

Matrix Multiplication

Let us consider a two-dimensional matrix A, having i rows and k columns, and another two-dimensional matrix B, having k rows and j columns. The product matrix C will have i rows and j columns.

Thus, for two matrices to be conformable for multiplication, the number of columns in the first matrix must equal the number of rows in the second matrix. Each element Ci,j will be the sum of the multiplication of the ith row of A with the jth column of B.

This is represented in mathematical notation as:

The required program listing along with the sample execution is given below.

Example . 

Program Ch05n05.c

 /*Program to multiply two matrices*/

 #include <stdio.h>

 int main()
 {
    const int M=3;      // M=no of rows of A and no of rows of C
    const int N=2;      // N=no of cols of A and no of rows of B
    const int P=2;      // P=no of cols of B and no of cols of C
    int A[M][N],B[N][P],C[M][P];
    int i,j,k;

    /*Read in the values for the A matrix*/
    printf("\nEnter the elements of matrix A row-wise...\n");
    for(i=0;i<M;i++)
        for(j=0;j<N;j++)
            scanf("%d",&A[i][j]);

    /*Read in the values for the B matrix*/
    printf("\nEnter the elements of matrix B row-wise...\n");
    for(j=0;j<N;j++)
        for(k=0;k<P;k++)
            scanf("%d",&B[j][k]);

    /*Print the matrix A*/
    printf("\nThe matrix A as entered is...\n");
    for(i=0;i<M;i++)
    {
        for(j=0;j<N;j++)
            printf("%4d",A[i][j]);
        printf("\n");
    }

    /*Print the matrix B*/
    printf("\nThe matrix B as entered is...\n");
    for(j=0;j<N;j++)
    {
        for(k=0;k<P;k++)
            printf("%4d",B[j][k]);
        printf("\n");
    }

    /*Calculate the product of the two matrices into matrix C*/
    for(i=0;i<M;i++)
        for(j=0;j<N;j++)
        {
            C[i][j]=0;
            for(k=0;k<N;k++)
               C[i][j]=C[i][j]+A[i][k]*B[k][j];
        }

    /*Print the result matrix C*/
    printf("\nThe matrix C as a product of A and B is...\n");
    for(i=0;i<M;i++)
    {
        for(k=0;k<P;k++)
            printf("%4d",C[i][k]);
        printf("\n");
    }
    return 0;
 }

 End Program
EXECUTION (Boldface represents input)

Enter the elements of matrix A row-wise...
 1 5
 2 3
 4 4
Enter the elements of matrix B row-wise...
3 6
7 9
The matrix A as entered is...
   1   5
   2   3
   4   4
The matrix B as entered is...
   3   6
   7   9
The matrix C as a product of A and B is...
  38  51
  27  39
  40  60

If matrices of different dimensions are to be used, it is enough to change the variables M, N, and P.

Row-Major and Column-Major Order

Revisiting the storage of arrays in memory, we have already learnt that arrays of multiple dimensions are not arranged as a rectangular (for a two-dimensional array) matrix of elements with rows and columns. Instead, they are arranged in a linear sequence starting from the base address of the array. We now move on to the mapping function that the programming language use to access a referenced element of a multi-dimensional array. This mapping must transform the provided coordinates (indices) into an address in the linear sequence of memory. The nature of this transformation is dependent on how the programming language will store the multi-dimensional array. Taking a two-dimensional array as a simplified case for a multi-dimensional array, we find that the array can be stored in either of two ways:

  • column by column

  • row by row

The first type of ordering for the mapping is called a column-major order and the second one, a row-major order.

The mapping function for a row-major order of two-dimensional arrays is:

Addr(Ai,j) = Base(A) + i*MAXCOLS + j

where Base(A) gives the base address of the array A, and the element Ai,j is to be accessed. MAXCOLS is the number of columns of the array A.

The mapping function for a column-major order of two-dimensional arrays is:

Addr(Ai,j) = Base(A) + j*MAXROWS + i

where MAXROWS is the number of rows of the array A and other terms remain the same.

It is left as an exercise to the reader to derive the address formulae for row-major and column-major orders for general multi-dimensional arrays. For now, we present a program that will print the elements of a two-dimensional array in row-major and column-major orders.

Example . 

Program Ch05n06.c

 /*Program to print the elements of a 2-D array in
 *Row-major and Column —major orders
 */

 #include <stdio.h>

 int main()
 {
    int Temp[3][2];
    int i, j;

    /*Read in the values for the array Temp*/
    printf("\nEnter the elements of array Temp(3×2) row-wise...\n");
    for(i=0;i<3;i++)
        for(j=0;j<2;j++)
            scanf("%d",&Temp[i][j]);

    /*Print the array Temp*/
    printf("\nThe array Temp as entered is...\n");
    for(i=0;i<3;i++)
    {
        for(j=0;j<2;j++)
            printf("\t%d",Temp[i][j]);
        printf("\n");
    }

    /*Print the array Temp in row-major order*/
    printf("\nThe array as entered in row major order is...\n");
    for(i=0;i<3;i++)
    {
        for(j=0;j<2;j++)
            printf("\t%d",Temp[i][j]);
    }
    /*Print the array Temp in column-major order*/
    printf("\n\nThe array as entered in column major order is...\n");
    for(j=0;j<2;j++)
    {
        for(i=0;i<3;i++)
            printf("\t%d",Temp[i][j]);
    }
    return 0;
 }

 End Program
EXECUTION (Boldface represents input)

 Enter the elements of array Temp(3×2) row-wise...
 3 5
 2 1
 7 4
 The array Temp as entered is...
    3     5
    2     1
    7     4
 The array as entered in row-major order is...
    3     5    2     1     7    4
 The array as entered in row-major order is...
    3     2    7     5     1    4

Single-Dimensional Character Arrays (Strings)

In C, a string is a single-dimensional character array. A character in a string can, thus, be accessed as in any other array by referring to the index of the character within subscripts. Another technique involves usage of the pointer to character and will be discussed in the chapter on Pointers.

String processing in C can be viewed as a special case of array processing. Among its special characteristics is the usage of a special character to terminate a string. The character used to terminate a string is the ‘\0’, a character constant. This character constant is referred as NULL character. Any constant string of length, say l, is stored in memory using l+1 characters. The additional byte of storage is required for the terminating ‘\0’. A string can be defined by the syntax for defining arrays, viz.

char w[22];

After storage has been allocated, there is a number of different ways of putting character values into the string. We can do it character by character as in single-dimensional arrays, as follows:

w[0]='s';
w[1]='a';
w[2]='n';
w[3]='\0';

The following program reads in a line of characters, typed in from the keyboard, into a string and prints the string in reverse order. As an additional task, it also prints the sum of the ASCII values of the characters entered.

Example . 

Program Ch05n07.c

 /*Program to reverse an input string and print the ASCII sum
 *of the characters comprising the string
 */

 #include <stdio.h>

 int main()
 {
    const int MaxLine=100;     // Sets the limit of characters in string
    char Line[MaxLine];
    int i,Sum=0,ch;

    /*Read in the string*/
    printf("\nEnter a set of characters : \n");
    for(i=0;((ch=getchar())!='\n');++i)
        Line[i]=(char)ch;
    Line[i]='\0';

    /*Print the reverse of the line*/
    printf("\nThe reverse of the line entered is...\n");
    while(i>0)
        putchar(Line[--i]);

    /*Print the sum of the ASCII values*/
    for(i=0;Line[i]!='\0';++i)
        Sum+=Line[i];
    printf("\n\nThe sum of the ASCII values is : %d\n",Sum);
    return 0;
 }

End Program
EXECUTION (Boldface represents input)

Enter a set of characters:
austin
The reverse of the line entered is...
nitsua
The sum of the ASCII values is: 660

Explanation: The program makes use of getchar and putchar. Since the size of the character array is 100 and the NULL (‘\0’) character is always used to delimit a string, the array can hold strings of a maximum size of 99 characters. The typecast of ch to char is performed to avoid a possible compiler warning diagnostic in the terms of “Conversion may lose significant digits”. All narrowing conversions should be explicitly typecast. This is good programming practice.

Initialization of Strings

Recall that arrays can be initialized. This feature applies to character arrays as well. The C compiler allows for an initialization of the form:

char p[]="abc";

which is taken to be equivalent to the definition

char p[]={'a','b','c','\0'};

In the first definition, the NULL at the end of the string will be included implicitly if there is room for it or if no size was specified. Here are some examples:

...
// No room for the NULL
char str[5]="hello";
...
// Room present for the NULL
char str[6]= "hello";
...

gets( ) and puts( )

The gets function accepts an entire line of input from the standard input device (assumed to be the keyboard) and appends a null character ‘\0’. The line can contain any number of characters. The function name gets is short for ‘get a string’. The general form of gets is:

gets(p);

The p represents a character array (a string).

The puts function is the complement of gets. It displays a string on the standard output device. The puts function writes a string to the standard output, stdout. Similarly, the gets reads in a string from the standard input, stdin.

The main restriction with puts is that it is always printed on a new line each time the function is called to print a character array. The following program will illustrate the use of these two functions.

Example . 

Program Ch05n08.c

 /*Illustrating gets and puts functions*/

 #include <stdio.h>

 int main()
 {

    char s1[80];
    int outcome;

    printf("Enter a string : ");
    gets(s1);
    printf("The string is %s\n",s1);
    printf("Using puts(): ",s1);
    outcome=puts(s1);
    printf("\nThe value returned by puts( ) is %d.\n",outcome);
    return 0;
 }

End Program

It is left as an exercise to the reader to analyse the output of the program to understand how the functions work. It is to be noted that puts returns an integer, which represents the last character written.

Let us consider another example. The program separates each word from a line of input assuming that there is at least a space between two words. It is left to the reader to analyse what happens if more than one spaces in present between two words of the input line.

Example . 

Program Ch05n09.c

 /*Separate each word from the input*/

 #include <stdio.h>

 int main()
 {
    char line[80];
    int i;

    puts("Enter a line:");
    gets(line);
    puts("Line is : ");
    puts(line);
    for(i=0;line[i]!='\0';i++)
        if(line[i]!=' ')
            putchar(line[i]);
        else
            printf("\n");
    return 0;
 }

 End Program

sscanf( ) and sprintf( )

These two functions are special versions of scanf and printf that employ strings. The sscanf function accepts input from an existing string instead of the standard input. The function is, thus, used as a temporary storage location for input for the purpose of error checking. The general form of sscanf is:

sscanf(Buffer, Control_String, List of variables)

We can see that the function is similar to scanf except that the variables are read in from the buffer according to the specifications in the control string. This function is useful when we want to assemble a string from different data types or from different variables.

In contrast to sscanf, sprintf writes formatted output to a string rather than to the standard output device. The general form of sprintf is:

sprintf(Buffer, Control_String, List of variables)

Here, the contents of the variable list are written to the string buffer according to the format defined in the control string. This function can be used to create an array of strings, each of which is formatted. Consider the creation of an array of identification numbers (IDs) and names. The variables are read in from the keyboard, an ID number is selected from an available list of numbers, and a new array consisting of both names and numbers can be created from the different inputs. The statement to perform this task would look like:

sprintf(s[i],"%5d %s",id,name);

Thus, the primary difference between the normal and the string counterparts of the printf and scanf functions would be that while printf and scanf perform operations to and from the standard output and input device respectively, sprintf and sscanf take strings as the output and input devices. Thus, while the result of printf is printed on the terminal, the formatted output by using the same control string and variable list would result in a print to a character array, the buffer. Similar differences exist between scanf and sscanf.

Let us consider the following example, which uses sscanf and sprintf functions.

Example . 

Program Ch05n10.c

 #include <stdio.h>

 int main()
 {
    char s2[130];

    int i, j;
    printf("Enter two numbers : \n");
    gets(s2);
    sscanf(s2,"%d%d",&i,&j);
    printf("\nThe values are : %d and %d.\n",i,j);
    return 0;
 }

End Program

If the input to the above program is ‘65 20’, for example, sscanf will decompose the two integers as specified by the control string, and they will be stored separately as integers after reading them in from the string.

String Functions

Strings are an important aspects of programming. We can use them to write messages to the user, to read text files and generally to make interactions with users go smoothly. In this section, we shall have a look at some of the string-handling functions in C.

The standard library of C contains many useful string-handling functions. Although these functions are not part of the C language, they are available on most C systems. The function prototypes are declared in the standard header file, string.h. This file should be included when using the string-handling library functions.

strlen

The function strlen returns the number of characters in the string argument passed to it. The length returned does not include the terminating null character. The length of the string is returned as the number of characters before the terminating null. The following example uses strlen to find out the length of a string. Note that a blank in the input string in not allowed as we are using scanf to read the input string.

Example . 

Program Ch05n11.c

 #include <stdio.h>
 #include <string.h>

 int main()
 {
    char text [80];
    int length;

    printf("Enter a string : ");
    scanf("%s",text);
    length=strlen(text);
    printf("\nThe length of the string is : %d.\n",length);
    return 0;
 }

 End Program

This program prompts for a string. It then reads the string and displays the length of the string using the library function strlen. We can also build a function, say strlen1, which is the same as the standard library function strlen. The function is presented below.

int strlen1(register char *s){
   register unsigned n;
   for(n=0;*s!='\0';++s)
       ++n;
   return n;
}

strcmp and strncmp

These two functions are used to compare the lexicographic ordering of two strings. A lexicographic ordering is based on the position of the characters in the character set used. For the C language, this would refer to ordering based on the ASCII set.

The strcmp function takes two string arguments. Let us call these arguments s1 and s2, respectively. An integer is returned that is less than, equal to, or greater than zero, depending on whether s1 is lexicographically less than, equal to, or greater than s2. The following program illustrates the use of the strcmp function.

Example . 

Program Ch05n12.c

 #include <stdio.h>
 #include <string.h>

 int main()
 {
    char s1[80],s2[80];
    int outcome;
    printf("Enter two strings : ");
    scanf("%s%s",s1,s2);
    outcome=strcmp(s1,s2);
    printf("\nValue returned by strcmp is %d.\n",outcome);
    return 0;
 }

End Program

For inputs of ‘hello here’ and ‘hello there’, the program outputs the value returned by strcmp as -1, which is true to the lexicographic ordering of the two strings.

The function strncmp has three arguments. The first two arguments are two strings. The third is an integer that specifies the number of characters to compare. This function returns the same range of values as the function strcmp. An example is presented below.

Example . 

Program Ch05n13.c

 #include <stdio.h>
 #include <string.h>

 int main()
 {
    char s1[80],s2[80];
    int outcome;

    printf("Enter two strings : ");
    scanf("%s%s",s1,s2);
    outcome=strncmp(s1,s2,2);
    printf("\nValue returned by strncmp is %d.\n",outcome);
    return 0;
 }

End Program

For inputs of COME and COBOL, the program outputs the value returned by strncmp as 0, which is true to the lexicographic ordering of the two strings in regard to their first two characters as specified by strncmp. When comparing the first two characters, notice that the two strings are identical, and so, strncmp returns 0.

strcat and strncat

The strcat function adds the entire contents of its second argument to the end of the first argument. Both of its arguments are strings. The general format for the function is:

strcat(s1,s2);

For example, if s1 contains a string ‘abc’ and s2 contains ‘def’, after the execution of the function the string s1 contains ‘abcdef’. The programmer must ensure that enough memory has been allocated to s1 so that it can hold the result. After the string concatenation is over, the terminating null is placed at the end of s1.

Another library function strncat will perform a function similar to that done by strcat. However, strncat has three arguments – two strings (like strcat) and an integer specifying the number of characters of s2 to be added to the end of s1. The format for calling strncat is as follows:

strncat(s1,s2,n);

The following program will illustrate the use of the above two functions.

Example . 

Program Ch05n14.c

 #include <stdio.h>
 #include <string.h>

 int main()
 {
    char s1[80],s2[10];
    int n;

    printf("Enter two strings : ");
    scanf("%s%s",s1,s2);

    strcat(s1,s2);
    printf("\nString built by strcat is %s.",s1);
    printf("\nLength of the string built by strcat is %d.",strlen(s1));

    strncat(s1,s2,5);
    printf("\nString built by strncat using 5 characters is %s.\n",s1);
    return 0;
 }

 End Program

When the above program is provided with inputs ‘See’ and ‘Programming’, the string built by strcat is ‘SeeProgramming’ and the length of the string is 17. After strncat is used, the result in s1 is ‘SeeProgrammingProgr’.

strcpy and strncpy

The strcpy function takes two arguments – say s1 and s2 (both strings). The contents of the string s2 are copied into the s1. Thus, the first argument to strcpy represents the destination string and the second argument represents the source string. The programmer is responsible, as in all other string-handling functions, for ensuring that array bounds are not overrun since C does not provide bounds checking on an array.

The strncpy function takes three arguments – the destination and source strings (like strcpy), and the last argument is an integer representing the number of characters of s2 to copy into s1. If the number of characters to be copied from s2 exceeds the length of s2, the function will simply pad s1 with the null character. On the other hand, if there are fewer characters to be copied than there are in s2, the function will not terminate the new string with the null character. The following program shows strcpy and strncpy at work.

Example . 

Program Ch05n15.c

 #include <stdio.h>
 #include <string.h>

 int main(){
    char s1[80],s2[10];
    int n;

    printf("Enter two strings : ");
    scanf("%s%s",s1,s2);

    strcpy(s1,s2);
    printf("\nString created by strcpy is %s.",s1);
    printf("\nLength of the new string is %d.",strlen(s1));

    strncpy(s1,s2,5);
    s1[5]='\0';
    printf("\nString created by strncpy using 5 characters is %s.\n",s1);
    return 0;
 }

 End Program

Try running the program with different inputs to understand how the code works.

Two-Dimensional Character Arrays

In string manipulation, two-dimensional arrays can also be used, though it can be better represented by a pointer array, as we shall discuss when we come to the chapter on Pointers. The two-dimensional character array, also called a string array or an array of strings, is initialized like single-dimensional arrays. For example, a C program segment may contain the following declaration:

static char color[6][7]={"red","green","blue","white","black","yellow"};

Now, suppose that the following strings are to be stored in a string array:

    "January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"

These strings can be stored in a two-dimensional character type array; for example:

char Month[12][10];

Note that Month contains 12 month names, i.e., 12 rows. Each row must be large enough to store a string. The maximum characters are needed for the month of September, which has 9 characters. We know that the null character ‘\0’ is placed at the end of each string. So, each row has 10 characters to make the structure uniform for all strings. The following program will help the reader to understand the operations that can be performed on a two-dimensional array.

Example . 

Program Ch05n16.c

 /*Re-arranging strings*/

 #include <stdio.h>
 #include <string.h>

 void reorder(int n,char x[10][6])
 {
    char temp[10][6];
    int i,item;

    for(item=0;item<n-1;++item)
         for(i=item+1;i<n;++i)
             if(strcmp(&x[item][0],&x[i][0])>0)
             {
                 strcpy(&temp[0][0],&x[item][0]);
                 strcpy(&x[item][0],&x[i][0]);
                 strcpy(&x[i][0],&temp[0][0]);
             }
 }

 void reverse(int n,char x[10][6])
 {
     char temp[10][6];
     int i,item;

     for(item=0;item<n-1;++item)
          for(i=item+1;i<n;++i)
              if(strcmp(&x[item][0],&x[i][0])<0)
              {
                  strcpy(&temp[0][0],&x[item][0]);
                  strcpy(&x[item][0],&x[i][0]);
                  strcpy(&x[i][0],&temp[0][0]);
              }
 }

 int main()
    {
    char x[10][6];
    int i,n=0,choice;

    printf("Enter strings (type \"end\" when finished : \n");
    do {
        printf("string %d : ",n);
        scanf("%s",&x[n][0]);
        printf("\n%s\n",&x[n][0]);
    } while(strcmp(&x[n++][0],"end"));
    printf("\nEnter 1 for alphabetic, 0 for reverse alphabetic\n");
    scanf("%d",&choice);
    if(choice)
        reorder(--n,x);
    else
        reverse(--n,x);
    (choice==1)?printf("\nReorder") : printf("\nReverse");

    for(i=0;i<n;i++)
        printf("\nstring %d, %s",i+1,&x[i][0]);

    return 0;
 }

 End Program

Consider the problem of entering a list of strings and re-arranging them into either alphabetical or reverse alphabetical order. The program ch05n16.c performs this task and includes a menu for the user to select the re-arrangements that will be used each time the program is executed.

The reader is expected to try out different programs to improve his/her expertise on arrays. Later, the chapter on Pointers will explain arrays further.

Summary

The chapter presented arrays and operations on arrays. Single-dimensional arrays were introduced and the initialization techniques discussed. Multi-dimensional arrays were presented with a special focus on two-dimensional arrays. Matrix operations were also demonstrated while discussing two-dimensional arrays.

Single-dimensional character arrays are called strings. Strings have some special library functions for manipulation. Strings in C terminate with a null character. Two-dimensional string arrays are used to store arrays of strings. The relationship between pointers and arrays was not discussed in this chapter, and the subject has been deferred until after discussing pointers.

New Terminology Checklist

Matrices

Arrays

Strings

Notations

Multi-dimension

Exercises

1.

Write a program to compute the inverse of a matrix.

2.

Write a program to convert a string from lower case to upper case.

3.

Write a program to convert a decimal number to its roman equivalent.

4.

Write a program to convert a roman number to its decimal equivalent.

5.

Write a function strcat1 to simulate the working of strcat.

6.

Write a function to reverse a character array.

7.

Write a function to sort an integer array.

8.

Write a function to return the maximum element of an integer array.

9.

What is the difference between a string and an array?

10.

List out the differences between strncpy and strcpy.