Skip to content Skip to sidebar Skip to footer

Program to Read a String From the User and Print It Using a Character Pointer Offset Notation

Chapter 4. Pointers and Arrays

An array is a fundamental data structure congenital into C. A thorough understanding of arrays and their use is necessary to develop effective applications. Misunderstandings of assortment and arrow usage tin can result in hard-to-find errors and less than optimal performance in applications. Assortment and arrow notations are closely related to each other and can often be used interchangeably in the right context.

A common misconception is that an array and a pointer are completely interchangeable. An array name is not a pointer. Although an array name can be treated as a pointer at times, and array note tin be used with pointers, they are singled-out and cannot always be used in place of each other. Agreement this difference will help you avoid wrong employ of these notations. For example, although the proper name of an array used by itself volition return the array's address, we cannot use the proper noun by itself as the target of an assignment.

Arrays back up many parts of an application and can be single or multidimensional. In this chapter, we will address the fundamental aspects of arrays as they relate to pointers to provide you with a deep understanding of arrays and the various ways they can be manipulated with pointers. Yous volition see their employ in more advanced contexts throughout the book.

Nosotros start with a quick review of arrays and and then examine the similarities and differences between assortment and pointer notation. Arrays can be created using malloc blazon functions. These functions provide more flexibility than that afforded by traditional array declarations. We will come across how the realloc part can exist used to change the amount of memory allocated for an array.

Dynamically allocating memory for an array tin present challenges, especially when we are dealing with arrays with two or more dimensions, every bit we take to ensure that the array is allocated in contiguous memory.

Nosotros volition besides explore problems that tin can occur when passing and returning arrays. In nearly situations, the array'due south size must be passed and then the assortment tin be properly handled in a role. At that place is zilch inherent in an assortment'southward internal representation that determines its length. If we do non pass the length, the function has no standard ways of knowing where the assortment ends. Nosotros volition also examine how to create jagged arrays in C, although they are infrequently used. A jagged assortment is a two-dimensional array where each row may have a different number of columns.

To demonstrate these concepts, we volition use a vector for single-dimensional arrays and a matrix for ii-dimensional arrays. Vectors and matrices accept establish all-encompassing use in many areas, including analyzing electromagnetic fields, weather prediction, and in mathematics.

Quick Review of Arrays

An array is a contiguous collection of homogeneous elements that tin can be accessed using an index. Past face-to-face, we mean the elements of the array are adjacent to ane another in retentivity with no gaps between them. Past homogeneous, we mean they are all of the same type. Array declarations use a gear up of brackets and can possess multiple dimensions.

Ii-dimensional arrays are common, and we typically use the terms rows and columns to describe the position of an assortment'southward chemical element. Arrays with 3 or more dimensions are not as common but can be quite useful in some applications. A 2-dimensional assortment is not to be confused with an array of pointers. They are similar merely conduct slightly differently, every bit will exist shown in the sectionUsing a I-Dimensional Array of Pointers.

Variable length arrays were introduced in C99 version of C. Previously, techniques using the realloc part were used to support arrays whose sizes change. We illustrate the realloc function in the section Using the realloc Function to Resize an Assortment.

Note

Arrays have a stock-still size. When nosotros declare an assortment, we need to decide how large it should be. If nosotros specify too many elements, we waste space. If nosotros specify too few elements, we limit how many elements nosotros can process. The realloc part and variable length arrays provide techniques for dealing with arrays whose size needs to change. With a lilliputian work, we tin resize an array and apply just the right corporeality of memory.

Ane-Dimensional Arrays

A ane-dimensional array is a linear construction. Information technology uses a single index to access its members. The following is a declaration of a 5-element array of integers:

              int              vector              [              5              ];            

Array indexes start with 0 and end at i less than their alleged size. Valid indexes for the array vector start at 0 and end at 4. All the same, C does not enforce these bounds. Using an invalid index for an assortment can event in unpredictable behavior. Figure 4-1 illustrates how the assortment is allocated in memory. Each chemical element is iv bytes in length and is uninitialized. Depending on the memory model used, as explained in Memory Models, the size may be different.

Array memory allocation

Figure iv-1. Array retention allocation

The internal representation of an array has no information nigh the number of elements it contains. The array proper name simply references a block of memory. Using the sizeof operator with an array will return the number of bytes allocated to the array. To determine the number of elements, we separate the array's size past its chemical element's size, as illustrated below. This will display 5:

              printf              (              "%d              \north              "              ,              sizeof              (              vector              )              /              sizeof              (              int              ));            

One-dimensional arrays tin exist readily initialized using a block type statement. In the following sequence, each element is initialized to an integer starting at ane:

              int              vector              [              v              ]              =              {              1              ,              ii              ,              3              ,              four              ,              5              };            

2-Dimensional Arrays

2-dimensional arrays use rows and columns to identify array elements. This type of assortment needs to be mapped to the one-dimension address space of principal memory. In C this is achieved by using a row-column ordering sequence. The assortment'southward offset row is placed in retentiveness followed by the second row, then the third row, and this ordering continues until the concluding row is placed in memory.

The following declares a two-dimensional array with two rows and iii columns. The assortment is initialized using a cake argument. Figure 4-2 illustrates how retentiveness is allocated for this array. The diagram on the left shows how memory is mapped. The diagram on the right shows how it can be viewed conceptually:

              int              matrix              [              2              ][              iii              ]              =              {{              ane              ,              ii              ,              3              },{              iv              ,              five              ,              6              }};            

Two-dimensional array

Figure 4-2. Two-dimensional assortment

A two-dimensional array is treated as an assortment of arrays. That is, when we access the array using just one subscript, we get a arrow to the corresponding row. This is demonstrated in the post-obit code sequence where each row'due south address and size is displayed:

              for              (              int              i              =              0              ;              i              <              2              ;              i              ++              )              {              printf              (              "&matrix[%d]: %p  sizeof(matrix[%d]): %d              \due north              "              ,              i              ,              &              matrix              [              i              ],              i              ,              sizeof              (              matrix              [              i              ]));              }            

The following output assumes the array is located at accost 100. The size is 12 because each row has three elements of four bytes each:

&matrix[0]: 100 sizeof(matrix[0]): 12 &matrix[one]: 112 sizeof(matrix[1]): 12

In the section Pointers and Multidimensional Arrays, nosotros will examine this behavior in more item.

Multidimensional Arrays

Multidimensional arrays have two or more than dimensions. As with 2-dimensional arrays, multiple sets of brackets define the array'due south blazon and size. In the following example, we define a three-dimensional array consisting of iii rows, two columns, and a rank of 4. The term rank is often used to denote the elements of the third dimension:

              int              arr3d              [              iii              ][              2              ][              4              ]              =              {              {{              one              ,              2              ,              3              ,              4              },              {              5              ,              6              ,              7              ,              8              }},              {{              nine              ,              x              ,              11              ,              12              },              {              thirteen              ,              fourteen              ,              15              ,              16              }},              {{              17              ,              eighteen              ,              xix              ,              20              },              {              21              ,              22              ,              23              ,              24              }}              };            

The elements are allocated contiguously in row-column-rank order equally illustrated in Figure 4-3.

Three-dimensional array

Figure 4-iii. Three-dimensional array

We will use these declarations in later examples.

Pointer Notation and Arrays

Pointers can be very useful when working with arrays. We can use them with existing arrays or to allocate retentivity from the heap and then care for the memory as if it were an assortment. Array annotation and arrow notation tin can be used somewhat interchangeably. Nevertheless, they are non exactly the same as detailed in the section Differences Between Arrays and Pointers.

When an array proper noun is used by itself, the array's address is returned. Nosotros tin can assign this address to a pointer as illustrated below:

            int            vector            [            5            ]            =            {            1            ,            two            ,            3            ,            4            ,            5            };            int            *            pv            =            vector            ;          

The variable pv is a arrow to the first chemical element of the array and not the array itself. When we showtime assigned a value to pv, we assigned the address of the array's kickoff element.

We tin can utilise either the array name by itself or use the accost-of operator with the array's first element equally illustrated beneath. These are equivalent and will render the address of vector. Using the address-of operator is more verbose but too more than explicit:

            printf            (            "%p            \n            "            ,            vector            );            printf            (            "%p            \due north            "            ,            &            vector            [            0            ]);          

The expression &vector is sometimes used to obtain the address of an array. Information technology differs from the other notations in that it returns a pointer to the entire array. The other ii approaches yield a pointer to an integer. Instead of returning a arrow to an integer, it returns a pointer to an array of integers. The apply of this type will be illustrated in the section Passing a Multidimensional Array.

Nosotros tin can also use array subscripts with pointers. Effectively, the notation pv[i] is evaluated as:

            *            (            pv            +            i            )          

The pointer pv contains the address of a block of memory. The bracket notation will take the address contained in pv and adds the value independent in the index i using pointer arithmetic. This new accost is then dereferenced to return its contents.

As we discussed in the section Arrow Arithmetic, calculation an integer to a pointer will increment the accost it holds by the product of the integer and the data type's size. The same is true if we add an integer to the proper noun of an array. The following 2 statements are equivalent:

            *            (            pv            +            i            )            *            (            vector            +            i            )          

Presume the vector is located at address 100 and pv is located at address 96. Tabular array 4-1 and Figure 4-4 illustrate the use of array subscripts and pointer arithmetic with both the array name and the pointer for various values.

Tabular array iv-1. Array/pointer note

Value Equivalent Expression
92 &vector[-2] vector - ii &pv[-2] pv - 2
100 vector vector+0 &pv[0] pv
100 &vector[0] vector+0 &pv[0] pv
104 &vector[1] vector + one &pv[1] pv + one
140 &vector[10] vector + 10 &pv[x] pv + x

Array/pointer notation

Effigy 4-4. Array/arrow notation

When we add 1 to the assortment address we effectively add 4, the size of an integer, to the address since this is an array of integers. With the offset and last operations, nosotros addressed locations outside the array's premises. While this is non a good practice, information technology does emphasize the need to be careful when using indexes or pointers to admission elements of an array.

Array notation tin can exist thought of as a "shift and dereference" operation. The expression vector[2] means start with vector, which is a arrow to the beginning of the assortment, shift two positions to the correct, and and so dereference that location to fetch its value. Using the address-of operator in conjunction with array notation, every bit in &vector[2], essentially cancels out the dereferencing. It can be interpreted as become left two positions and then return that address.

The post-obit demonstrates the use of pointers in the implementation of the scalar addition functioning. This operation takes a value and multiplies information technology against each element of the vector:

            pv            =            vector            ;            int            value            =            3            ;            for            (            int            i            =            0            ;            i            <            5            ;            i            ++            )            {            *            pv            ++            *=            value            ;            }          

Differences Between Arrays and Pointers

There are several differences between the use of arrays and the use of pointers to arrays. In this department, we volition use the vector assortment and pv pointer as defined beneath:

              int              vector              [              five              ]              =              {              1              ,              2              ,              3              ,              four              ,              v              };              int              *              pv              =              vector              ;            

The lawmaking generated by vector[i] is different from the code generated by vector+i. The annotation vector[i] generates machine code that starts at location vector, moves i positions from this location, and uses its content. The note vector+i generates machine code that starts at location vector, adds i to the address, and and so uses the contents at that address. While the result is the same, the generated machine code is dissimilar. This divergence is rarely of significance to virtually programmers.

There is a difference when the sizeof operator is practical to an array and to a pointer to the same array. Applying the sizeof operator to vector will return 20, the number of bytes allocated to the array. Applying the sizeof operator against pv will return 4, the pointer's size.

The pointer pv is an lvalue. An lvalue denotes the term used on the lefthand side of an assignment operator. An lvalue must exist capable of being modified. An array name such as vector is non an lvalue and cannot exist modified. The address assigned to an array cannot be inverse . A arrow tin can exist assigned a new value and reference a different section of memory.

Consider the following:

              pv              =              pv              +              1              ;              vector              =              vector              +              1              ;              // Syntax fault            

Nosotros cannot modify vector, merely its contents. Nonetheless, the expression vector+1 is fine, as demonstrated below:

              pv              =              vector              +              i              ;            

Using malloc to Create a One-Dimensional Assortment

If we classify retentiveness from the heap and assign the address to a arrow, at that place is no reason we cannot use array subscripts with the pointer and treat this memory every bit an array. In the following sequence, nosotros indistinguishable the contents of the vector array used earlier:

            int            *            pv            =            (            int            *            )            malloc            (            5            *            sizeof            (            int            ));            for            (            int            i            =            0            ;            i            <            5            ;            i            ++            )            {            pv            [            i            ]            =            i            +            1            ;            }          

We could have used pointer notation as shown beneath; however, the assortment notation is often easier to follow:

            for            (            int            i            =            0            ;            i            <            v            ;            i            ++            )            {            *            (            pv            +            i            )            =            i            +            1            ;            }          

Figure 4-five illustrates how retention is allocated for this example.

Array allocated from the heap

Figure 4-v. Array allocated from the heap

This technique creates a region of memory and treats it as an array. Its size is determined at runtime. However, we demand to remember to deallocate the memory when we are through with it.

Alarm

In the previous example we used *(pv+i) instead of *pv+i . Since the dereference operator has college precedence than the plus operator, the second expression'south arrow is dereferenced, giving us the value referenced past the arrow. Nosotros and so add i to this integer value. This was non what was intended. In addition, when we use this expression as an lvalue, the compiler volition complain. Thus, we demand to force the add-on to be performed first, followed by the dereference functioning, in order for it to work correctly.

Using the realloc Function to Resize an Array

We can resize an existing array created using malloc with the realloc function. The essentials of the realloc role were detailed in Chapter two. The C standard C99 supports variable length arrays. In some situations, this may testify to be a better solution than using the realloc function. If yous are not using C99, then the realloc function volition need to be used. Also, variable length arrays can only be declared as a member of a function. If the array is needed longer than the function's elapsing, then realloc will demand to be used.

To illustrate the realloc part, we will implement a function to read in characters from standard input and assign them to a buffer. The buffer will incorporate all of the characters read in except for a terminating return graphic symbol. Since we do not know how many characters the user will input, nosotros do not know how long the buffer should be. We volition use the realloc role to allocate additional space by a stock-still increment amount. The code to implement this part is shown below:

            char            *            getLine            (            void            )            {            const            size_t            sizeIncrement            =            x            ;            char            *            buffer            =            malloc            (            sizeIncrement            );            char            *            currentPosition            =            buffer            ;            size_t            maximumLength            =            sizeIncrement            ;            size_t            length            =            0            ;            int            character            ;            if            (            currentPosition            ==            Nothing            )            {            return            NULL            ;            }            while            (            1            )            {            character            =            fgetc            (            stdin            );            if            (            character            ==            '\n'            )            {            interruption            ;            }            if            (            ++            length            >=            maximumLength            )            {            char            *            newBuffer            =            realloc            (            buffer            ,            maximumLength            +=            sizeIncrement            );            if            (            newBuffer            ==            NULL            )            {            free            (            buffer            );            return            NULL            ;            }            currentPosition            =            newBuffer            +            (            currentPosition            -            buffer            );            buffer            =            newBuffer            ;            }            *            currentPosition            ++            =            character            ;            }            *            currentPosition            =            '\0'            ;            return            buffer            ;            }          

Nosotros volition get-go past defining a series of declarations every bit summarized in Tabular array iv-2.

Tabular array 4-2. getLine variables

sizeIncrement The size of the initial buffer and the amount it will be incremented by when the buffer needs to be enlarged
buffer A arrow to the characters read in
currentPosition A pointer to the next costless position in the buffer
maximumLength The maximum number of characters that can be safely stored in the buffer
length The number of characters read in
character The last character read in

The buffer is created with a size of sizeIncrement. If the malloc role is unable to classify memory, the starting time if argument will force the office to return NULL. An infinite loop is entered where the characters are candy one at a fourth dimension. When the loop exits, a NUL is added to terminate the string and the buffer's accost is returned.

Within the while loop, a character is read in. If it is a wagon return, the loop is exited. Next, the if argument determines whether nosotros have exceeded the buffer'southward size. Otherwise, the character is added to the current position within the buffer.

If we accept exceeded the buffer's size, the realloc function creates a new block of retentivity. This block is sizeIncrement bytes larger than the sometime one. If it is unable to allocate memory, nosotros free upwards the existing allocated memory and force the function to return NULL. Otherwise, currentPosition is adjusted to bespeak to the right position within the new buffer and we assign the variable buffer to point to the newly allocated buffer. The realloc function will not necessarily keep your existing retentiveness in place, so y'all accept to use the pointer it returns to effigy out where your new, resized memory block is.

The variable newBuffer holds the allocated retentivity's address. We needed a separate variable, non buffer, in case the realloc was unable to classify memory. This allows us to detect and handle the condition.

We did non gratuitous buffer if realloc was successful considering realloc will re-create the original buffer to the new buffer and free up the old buffer. If we had tried to free buffer, then it would ordinarily effect in the programme's termination because we tried to free the same block of retention twice.

Effigy iv-6 illustrates retention being allocated for the getLine role with an input cord of "Once upon a time there was a giant pumpkin." The program stack has been simplified to ignore the local variables except for buffer and currentPosition. The buffer has been extended four times, every bit indicated by the rectangle containing the input string.

Memory allocation for getLine function

Figure 4-vi. Memory resource allotment for getLine function

The realloc function can as well be used to decrease the amount of space used by a pointer. To illustrate its employ, the trim function shown below will remove leading blanks in a cord:

            char            *            trim            (            char            *            phrase            )            {            char            *            onetime            =            phrase            ;            char            *            new            =            phrase            ;            while            (            *            old            ==            ' '            )            {            old            ++            ;            }            while            (            *            quondam            )            {            *            (            new            ++            )            =            *            (            quondam            ++            );            }            *            new            =            0            ;            render            (            char            *            )            realloc            (            phrase            ,            strlen            (            phrase            )            +            one            );            }            int            main            ()            {            char            *            buffer            =            (            char            *            )            malloc            (            strlen            (            "  cat"            )            +            ane            );            strcpy            (            buffer            ,            "  true cat"            );            printf            (            "%s            \northward            "            ,            trim            (            buffer            ));            }          

The beginning while loop uses the tmp variable to skip over any leading blanks. The second while loop copies the remaining characters in the cord to the beginning of the cord. It will evaluate to true until NUL is reached, which will evaluate to false. A nil is then added to end the string. The realloc function is so used to reallocate the retention based on the cord'due south new length.

Effigy iv-7 illustrates the function's utilize with an original string of "cat." The state of string earlier and after the trim office executes is shown. The memory in cerise is the old retention and should not be accessed.

Realloc example

Figure 4-7. Realloc example

Passing a One-Dimensional Assortment

When a one-dimensional array is passed to a function, the assortment's address is passed by value. This makes the transfer of information more efficient since nosotros are non passing the entire array and having to allocate memory in the stack for it. Normally, this means the array's size must be passed. If we don't, from the office'southward perspective all we have is the accost of an array with no indication of its size.

Unless there is something integral to the array to tell u.s.a. its premises, we need to pass the size data when we pass the array. In the instance of a string stored in an array, nosotros tin rely on the NUL termination graphic symbol to tell us when we tin can stop processing the array. We will examine this in Chapter v. By and large, if nosotros do non know the array'due south size, we are unable to process its elements and can current of air upwards working with too few elements or treating retentivity outside of the array every bit if it were part of the array. This will oft upshot in abnormal plan termination.

Nosotros can declare the array in the office declaration using ane of two notations: assortment notation or pointer notation.

Using Assortment Notation

In the following example, an integer array is passed to a part along with its size. Its contents are then displayed:

              void              displayArray              (              int              arr              [],              int              size              )              {              for              (              int              i              =              0              ;              i              <              size              ;              i              ++              )              {              printf              (              "%d              \n              "              ,              arr              [              i              ]);              }              }              int              vector              [              v              ]              =              {              1              ,              2              ,              three              ,              4              ,              five              };              displayArray              (              vector              ,              five              );            

The sequence'southward output will be the numbers i through 5.We passed the number five to the function that indicates its size. Nosotros could have passed any positive number and the function would attempt to display the corresponding number of elements, regardless of whether the size was correct. The program may finish if we attempt to accost memory outside of the array's bounds. The memory allocation for this example is shown in Effigy iv-8.

Using array notation

Figure four-8. Using array notation

Warning

A common mistake is to apply the sizeof operator with the assortment in gild to make up one's mind its number of elements, as shown below. Still, as explained in the sectionOne-Dimensional Arrays, this is non the correct mode of determining its size. In this case, we would be passing the value of 20 to the array.

                displayArray                (                arr                ,                sizeof                (                arr                ));              

Information technology is a common practice to pass a size smaller than the actual number of elements in an array. This is done to process only function of an assortment. For example, presume we read in a series of ages into an assortment simply did not fill up the assortment. If we chosen a sort function to sort it, nosotros would just want to sort the valid ages, not every assortment element.

Using Pointer Notation

Nosotros do not accept to use the bracket annotation when declaring an array parameter of a function. Instead, nosotros can utilize pointer notation equally follows:

              void              displayArray              (              int              *              arr              ,              int              size              )              {              for              (              int              i              =              0              ;              i              <              size              ;              i              ++              )              {              printf              (              "%d              \northward              "              ,              arr              [              i              ]);              }              }            

We connected to use array notation within the function. If desired, nosotros could accept used pointer notation in the function:

              void              displayArray              (              int              *              arr              ,              int              size              )              {              for              (              int              i              =              0              ;              i              <              size              ;              i              ++              )              {              printf              (              "%d              \due north              "              ,              *              (              arr              +              i              ));              }              }            

If we had used array notation to declare the part, we could have notwithstanding used pointer note in the function'due south trunk:

              void              displayArray              (              int              arr              [],              int              size              )              {              for              (              int              i              =              0              ;              i              <              size              ;              i              ++              )              {              printf              (              "%d              \n              "              ,              *              (              arr              +              i              ));              }              }            

Using a 1-Dimensional Array of Pointers

In this section, we will examine the key aspects of using an array of pointers by using an array of pointers to integer. Examples of array of pointers tin also exist found in:

  • Using an Assortment of Function Pointers, where we apply an array of office pointers;

  • How Memory Is Allocated for a Construction, where an array of structures is used; and

  • Passing Arguments to an Awarding, where the argv array is handled.

The purpose of this section is to set the stage for later examples by illustrating the essence of the approach. The following sequence declares an assortment of integer pointers, allocates memory for each element, and initializes this retentiveness to the assortment'due south index:

            int            *            arr            [            5            ];            for            (            int            i            =            0            ;            i            <            5            ;            i            ++            )            {            arr            [            i            ]            =            (            int            *            )            malloc            (            sizeof            (            int            ));            *            arr            [            i            ]            =            i            ;            }          

If this array was displayed, the numbers 0 through 4 would exist printed. Nosotros used arr[i] to reference the pointer and *arr[i] to assign a value to the location referenced by the arrow. Do not let the use of array note confuse yous. Since arr was declared as an array of pointers, arr[i] returns an address. When we dereference a arrow such as *arr[i], nosotros get the contents at that address.

We could have used the following equivalent arrow notation for the loop's torso:

            *            (            arr            +            i            )            =            (            int            *            )            malloc            (            sizeof            (            int            ));            **            (            arr            +            i            )            =            i            ;          

This notation is harder to follow, but agreement it will further your C expertise. Nosotros are using ii levels of indirection in the second statement. Mastery of this type of notation will split yous from the less experienced C programmers.

The subexpression (arr+i) represents the accost of the array's ith chemical element. We need to alter the content of this accost so we use the subexpression *(arr+i). The allocated memory is assigned to this location in the kickoff statement. Dereferencing this subexpression a 2nd fourth dimension, as we do in the second statement, returns the allocated retentiveness'due south location. We so assign the variable i to it. Figure iv-nine illustrates how memory is allocated.

For instance, arr[1] is located at address 104. The expression (arr+1) will requite us 104. Using *(arr+i) gives us its content. In this example, it is the arrow 504. Dereferencing this a 2d time using **(arr+1) gives u.s.a. the contents of 504, which is a 1.

Array of pointers

Figure 4-9. Array of pointers

Case expressions are listed in Table 4-3. Reading arrow expression from left to right and not ignoring parentheses can assistance in understanding how they work.

Table iv-three. Array of pointers expressions

Expression Value
*arr[0] 0
**arr 0
**(arr+ane) 1
arr[0][0] 0
arr[3][0] three

The first 3 expressions are similar to those in the previous caption. The last two are unlike. The utilise of a pointer to a arrow annotation suggests nosotros are dealing with an assortment of pointers. In upshot, this is what nosotros are doing. If we reexamine Figure four-9 and pretend each element of arr points to an array of size ane, then the last ii expressions make sense. What we take is a v-element array of pointers to a serial of one-element arrays.

The expression arr[3][0] refers to the quaternary element of arr and and so the start chemical element of the array it points to. The expression arr[3][1] does non work because the array the fourth element is pointing to does not accept two elements.

This suggests the power to create jagged arrays. This is indeed possible and is the subject of the sectionJagged Arrays and Pointers.

Pointers and Multidimensional Arrays

Parts of multidimensional arrays can be treated as subarrays. For case, each row of a two-dimensional array can be treated equally a one-dimensional array. This behavior affects how we use pointers when dealing with multidimensional arrays.

To illustrate this beliefs, nosotros create a two-dimensional array and initialize it as follows:

            int            matrix            [            2            ][            v            ]            =            {{            1            ,            2            ,            iii            ,            4            ,            v            },{            6            ,            seven            ,            eight            ,            ix            ,            10            }};          

The addresses and their corresponding values are then displayed:

            for            (            int            i            =            0            ;            i            <            2            ;            i            ++            )            {            for            (            int            j            =            0            ;            j            <            5            ;            j            ++            )            {            printf            (            "matrix[%d][%d]  Address: %p  Value: %d            \n            "            ,            i            ,            j            ,            &            matrix            [            i            ][            j            ],            matrix            [            i            ][            j            ]);            }            }          

The output follows:

matrix[0][0]  Accost: 100  Value: 1 matrix[0][1]  Accost: 104  Value: two matrix[0][2]  Address: 108  Value: iii matrix[0][3]  Address: 112  Value: 4 matrix[0][4]  Address: 116  Value: five matrix[1][0]  Accost: 120  Value: vi matrix[1][1]  Address: 124  Value: 7 matrix[one][2]  Accost: 128  Value: 8 matrix[i][3]  Accost: 132  Value: 9 matrix[one][4]  Address: 136  Value: x

The assortment is stored in row-column lodge. That is, the first row is stored sequentially in memory followed past the second row. The memory allocation is illustrated in Figure four-10.

We can declare a arrow for employ with this array as follows:

            int            (            *            pmatrix            )[            5            ]            =            matrix            ;          

Two-dimensional array memory allocation

Figure iv-ten. Two-dimensional array memory allocation

The expression, (*pmatrix), declares a pointer to an array. Combined with the residue of the declaration, pmatrix is defined as a arrow to a two-dimensional array of integers with 5 elements per cavalcade. If we had left the parentheses off, we would take alleged a five-element assortment of pointers to integers. The size of the first dimension is 2 since we know the dimensions of the matrix. If a different size is used to admission the assortment, then the results are unpredictable.

If nosotros desire to access the 2d chemical element, two, using pointer notation, information technology might seem reasonable to use the following:

            printf            (            "%p            \north            "            ,            matrix            );            printf            (            "%p            \northward            "            ,            matrix            +            ane            );          

The output follows:

100 120

The address returned past matrix+1 is non offset by four from the first of the array. Instead, it is starting time by the first row's size, twenty bytes. Using matrix by itself returns the address of the assortment'south showtime element. Since a two-dimensional array is an array of arrays, we get the address of a 5-element integer array. Its size is 20. Nosotros can verify this with the post-obit statement, which will display twenty:

            printf            (            "%d            \north            "            ,            sizeof            (            matrix            [            0            ]));            // Displays 20          

To access the array's second element, we need to add 1 to the first row of the assortment as follows: *(matrix[0] + 1). The expression, matrix[0], returns the address of the outset element of the first row of the assortment. This address is the address of an array of integers. Thus, when we add one to it, the size of a single integer is added to it, giving united states of america the second chemical element. The output will be 104 and ii.

            printf            (            "%p  %d            \due north            "            ,            matrix            [            0            ]            +            1            ,            *            (            matrix            [            0            ]            +            1            ));          

We tin can graphically depict the array every bit illustrated in Effigy 4-11.

Graphically depiction of a two-dimensional array

Figure 4-11. Graphically depiction of a two-dimensional array

Two-dimensional array notation tin can be interpreted every bit shown in Effigy 4-12.

Two-dimensional array notation

Figure 4-12. Two-dimensional array notation

Passing a Multidimensional Assortment

Passing a multidimensional assortment to a function tin can be confusing, especially when pointer note is used. When passing a multidimensional array, we need to make up one's mind whether to use array note or arrow notation in the function's signature. Some other consideration is how to convey the assortment's shape. By shape, we are referring to the number and size of its dimensions. If we want to use array notation within the function, information technology is imperative to specify the array's shape. Otherwise, the compiler is unable to use subscripts.

To pass the matrix assortment, utilise either:

            void            display2DArray            (            int            arr            [][            5            ],            int            rows            )            {          

or:

            void            display2DArray            (            int            (            *            arr            )[            five            ],            int            rows            )            {          

In both versions the number of columns is specified. This is needed considering the compiler needs to know the number of elements in each row. If this information is non passed, then it is unable to evaluate expressions such as arr[0][iii] equally explained in the section Pointers and Multidimensional Arrays.

In the first version, the expression arr[] is an implicit proclamation of a pointer to an array. In the 2nd version, the expression (*arr) is an explicit announcement of the pointer.

Warning

The following announcement will not piece of work correctly:

              void              display2DArray              (              int              *              arr              [              5              ],              int              rows              )              {            

While information technology will not generate a syntax error, the assortment passed is assumed to be a five-chemical element assortment of pointers to integers. Using a One-Dimensional Assortment of Pointers discusses arrays of pointers.

A simple implementation of this role and invocation follows:

            void            display2DArray            (            int            arr            [][            5            ],            int            rows            )            {            for            (            int            i            =            0            ;            i            <            rows            ;            i            ++            )            {            for            (            int            j            =            0            ;            j            <            5            ;            j            ++            )            {            printf            (            "%d"            ,            arr            [            i            ][            j            ]);            }            printf            (            "            \n            "            );            }            }            void            main            ()            {            int            matrix            [            two            ][            5            ]            =            {            {            1            ,            2            ,            3            ,            4            ,            v            },            {            6            ,            7            ,            8            ,            9            ,            ten            }            };            display2DArray            (            matrix            ,            two            );            }          

The function does non allocate memory for the array. Only the address is passed. The program stack'due south land for this call is shown in Effigy 4-13.

Passing multidimensional array

Figure 4-xiii. Passing multidimensional assortment

You may run into a office declared as follows. It is passed a single arrow and the number of rows and columns:

            void            display2DArrayUnknownSize            (            int            *            arr            ,            int            rows            ,            int            cols            )            {            for            (            int            i            =            0            ;            i            <            rows            ;            i            ++            )            {            for            (            int            j            =            0            ;            j            <            cols            ;            j            ++            )            {            printf            (            "%d "            ,            *            (            arr            +            (            i            *            cols            )            +            j            ));            }            printf            (            "            \n            "            );            }            }          

The printf statement calculates the accost of each element by adding to arr the number of elements in the previous row(s), (i*cols), and then calculation j to specify the column. To invoke the part, we can utilize the post-obit:

            display2DArrayUnknownSize            (            &            matrix            [            0            ][            0            ],            two            ,            5            );          

Within the function, nosotros cannot utilise assortment subscripts as shown below:

            printf            (            "%d "            ,            arr            [            i            ][            j            ]);          

This is not possible because the pointer is not declared as a two-dimensional array. However, it is possible to utilize array annotation as shown below. Nosotros tin use a unmarried subscript since it will exist interpreted simply as an offset within the assortment, whereas two subscripts cannot be used because the compiler doesn't know the size of the dimensions:

            printf            (            "%d "            ,            (            arr            +            i            )[            j            ]);          

The beginning element's accost is passed using &matrix[0][0] instead of matrix. While using matrix will execute correctly, a warning volition exist generated, indicating incompatible arrow types. The expression &matrix[0][0] is a pointer to an integer, whereas matrix is a pointer to an assortment of integers.

When passing an array with more than two dimensions, all but the size of the first dimension need to be specified. The post-obit demonstrates a function written to display a three-dimensional array. The final ii dimensions are specified in the proclamation:

            void            display3DArray            (            int            (            *            arr            )[            ii            ][            four            ],            int            rows            )            {            for            (            int            i            =            0            ;            i            <            rows            ;            i            ++            )            {            for            (            int            j            =            0            ;            j            <            ii            ;            j            ++            )            {            printf            (            "{"            );            for            (            int            k            =            0            ;            yard            <            4            ;            thou            ++            )            {            printf            (            "%d "            ,            arr            [            i            ][            j            ][            g            ]);            }            printf            (            "}"            );            }            printf            (            "            \n            "            );            }            }          

The following lawmaking shows the function'south invocation:

            int            arr3d            [            3            ][            2            ][            4            ]            =            {            {{            ane            ,            2            ,            3            ,            4            },            {            5            ,            6            ,            7            ,            viii            }},            {{            9            ,            10            ,            xi            ,            12            },            {            13            ,            14            ,            15            ,            16            }},            {{            17            ,            eighteen            ,            19            ,            20            },            {            21            ,            22            ,            23            ,            24            }}            };            display3DArray            (            arr3d            ,            three            );          

The output follows:

{ane 2 3 4 }{five 6 7 8 } {9 10 xi 12 }{13 14 xv sixteen } {17 18 19 20 }{21 22 23 24 }

Allocation of the array's memory is depicted in Effigy 4-14.

Three-dimensional array

Figure 4-fourteen. Iii-dimensional array

The expression arr3d[i] refers to the array'due south 2nd row and is a arrow to a ii-dimensional array with ii rows and four columns. The expression arr3d[ane][0] refers to the 2nd row, commencement cavalcade of the array and is a pointer to a one-dimensional array of size 5.

Dynamically Allocating a 2-Dimensional Array

Several issues are involved with dynamically allocating retention for a two-dimensional array, including:

  • Whether the array elements demand to be contiguous

  • Whether the assortment is jagged

Retentivity is allocated contiguously when a ii-dimensional assortment is alleged every bit follows:

            int            matrix            [            two            ][            five            ]            =            {{            1            ,            2            ,            3            ,            4            ,            five            },{            6            ,            seven            ,            eight            ,            9            ,            10            }};          

However, when we utilize a function such as malloc to create a 2-dimensional array, at that place are variations in how retentiveness can be allocated. Since a two-dimensional array can be treated as an array of arrays, at that place is no reason the "inner" arrays need to exist contiguous. When assortment subscripts are used with such an assortment, the assortment'south noncontiguous nature is handled transparently.

Note

Whether or not it is contiguous can bear on other operations, such as copying a cake of retentivity. Multiple copies may be required if the retention is not face-to-face.

Allocating Potentially Noncontiguous Memory

The following illustrates one way of allocating a 2-dimensional assortment where the allocated retention is not guaranteed to be contiguous. Starting time, the "outer" assortment is allocated and then each row is allocated using carve up malloc statements:

              int              rows              =              ii              ;              int              columns              =              5              ;              int              **              matrix              =              (              int              **              )              malloc              (              rows              *              sizeof              (              int              *              ));              for              (              int              i              =              0              ;              i              <              rows              ;              i              ++              )              {              matrix              [              i              ]              =              (              int              *              )              malloc              (              columns              *              sizeof              (              int              ));              }            

Since split up malloc calls were used, the allocated retentiveness is not guaranteed to be contiguous. This is illustrated in Effigy 4-15.

Noncontiguous allocation

Effigy iv-15. Noncontiguous allocation

The bodily allocation depends on the heap managing director and the heap'south state. It may well be contiguous.

Allocating Contiguous Memory

We will present two approaches for allocating contiguous memory for a two-dimensional array. The showtime technique allocates the "outer" array first and then all of the memory for the rows. The second technique allocates all of the memory at once.

The start technique is illustrated in the following sequence. The first malloc allocates an array of pointers to integers. Each chemical element volition be used to hold a arrow to a row. This is the block allocated at address 500 in Figure iv-16. The 2nd malloc allocates retentivity for all of the elements of the array at location 600. In the for loop, each element of the first assortment is assigned a portion of the memory allocated by the second m alloc:

              int              rows              =              ii              ;              int              columns              =              5              ;              int              **              matrix              =              (              int              **              )              malloc              (              rows              *              sizeof              (              int              *              ));              matrix              [              0              ]              =              (              int              *              )              malloc              (              rows              *              columns              *              sizeof              (              int              ));              for              (              int              i              =              one              ;              i              <              rows              ;              i              ++              )              matrix              [              i              ]              =              matrix              [              0              ]              +              i              *              columns              ;            

Contiguous allocation with two malloc calls

Figure 4-16. Contiguous allotment with two malloc calls

Technically, the retention for the first array may exist separated from the memory for the array's "body." However, a face-to-face region of retentiveness is allocated for the torso.

In the 2nd technique shown beneath, all of the memory for the array is allocated at 1 time:

              int              *              matrix              =              (              int              *              )              malloc              (              rows              *              columns              *              sizeof              (              int              ));            

This resource allotment is illustrated in Figure 4-17.

Contiguous allocation with a single malloc call

Figure 4-17. Contiguous resource allotment with a single malloc phone call

When the array is referenced later in code, assortment subscripts cannot be used. Instead, indexes into the assortment need to be calculated manually, as illustrated in the following code sequence. Each assortment element is initialized to the product of its indexes:

              for              (              int              i              =              0              ;              i              <              rows              ;              i              ++              )              {              for              (              int              j              =              0              ;              j              <              columns              ;              j              ++              )              {              *              (              matrix              +              (              i              *              columns              )              +              j              )              =              i              *              j              ;              }              }            

Array subscripts cannot be used because we have lost the shape information needed by the compiler to permit subscripts. This concept is explained in the section Passing a Multidimensional Array.

This arroyo has limited utilise in the existent world, simply information technology does illustrate the relationship between the concept of a two-dimensional assortment and the one-dimensional nature of primary memory. The more convenient ii-dimensional array notation makes this mapping transparent and easier to employ.

We accept demonstrated two general approaches for allocating contiguous memory for a two-dimensional array. The approach to employ depends on the needs of the application. Nonetheless, the last arroyo generates a unmarried cake of memory for the "entire" array.

Jagged Arrays and Pointers

A jagged array is a two-dimensional array possessing a dissimilar number of columns for each row. Conceptually, this is illustrated in Figure 4-18, where the array has three rows with a varying number of columns per row.

Jagged array

Figure 4-18. Jagged array

Before we larn how to create such an assortment, let's examine a two-dimensional array created using compound literals. A compound literal is a C construct that consists of what appears to be a bandage operator followed by an initializer list enclosed in braces. An example of a compound literal follows for both a constant integer and an array of integers. These would be used as role of a announcement:

            (            const            int            )            {            100            }            (            int            [            3            ])            {            ten            ,            20            ,            30            }          

In the following declaration, nosotros create the assortment arr1 past declaring it as an array of pointers to an integer and using a block statement of compound literals to initialize information technology:

            int            (            *            (            arr1            []))            =            {            (            int            [])            {            0            ,            1            ,            two            },            (            int            [])            {            three            ,            4            ,            v            },            (            int            [])            {            6            ,            7            ,            8            }};          

This array has 3 rows and three columns. The array'due south elements are initialized with the value 0 through 8 in row column order. Figure 4-19 depicts how memory is laid out for this array.

Two-dimensional array

Figure 4-19. Ii-dimensional array

The following sequence displays the addresses and values of each array element:

            for            (            int            j            =            0            ;            j            <            3            ;            j            ++            )            {            for            (            int            i            =            0            ;            i            <            iii            ;            i            ++            )            {            printf            (            "arr1[%d][%d]  Address: %p  Value: %d            \n            "            ,            j            ,            i            ,            &            arr1            [            j            ][            i            ],            arr1            [            j            ][            i            ]);            }            printf            (            "            \n            "            );            }          

When executed, we will become the post-obit output:

arr1[0][0]  Address: 0x100  Value: 0 arr1[0][ane]  Address: 0x104  Value: one arr1[0][2]  Address: 0x108  Value: ii  arr1[one][0]  Address: 0x112  Value: 3 arr1[ane][1]  Address: 0x116  Value: 4 arr1[i][2]  Address: 0x120  Value: 5  arr1[2][0]  Address: 0x124  Value: 6 arr1[2][i]  Address: 0x128  Value: 7 arr1[ii][ii]  Address: 0x132  Value: eight

This declaration tin be modified slightly to create a jagged array every bit depicted in Figure 4-18. The array declaration follows:

            int            (            *            (            arr2            []))            =            {            (            int            [])            {            0            ,            i            ,            two            ,            3            },            (            int            [])            {            4            ,            5            },            (            int            [])            {            6            ,            7            ,            8            }};          

We used 3 compound literals to declare the jagged assortment. The array'due south elements are initialized in row-column lodge starting with a value of cypher. The next sequence will display the array to verify its cosmos. The sequence required three for loops because each row had a different number of columns:

            int            row            =            0            ;            for            (            int            i            =            0            ;            i            <            4            ;            i            ++            )            {            printf            (            "layer1[%d][%d]  Address: %p  Value: %d            \n            "            ,            row            ,            i            ,            &            arr2            [            row            ][            i            ],            arr2            [            row            ][            i            ]);            }            printf            (            "            \northward            "            );            row            =            1            ;            for            (            int            i            =            0            ;            i            <            2            ;            i            ++            )            {            printf            (            "layer1[%d][%d]  Address: %p  Value: %d            \north            "            ,            row            ,            i            ,            &            arr2            [            row            ][            i            ],            arr2            [            row            ][            i            ]);            }            printf            (            "            \due north            "            );            row            =            2            ;            for            (            int            i            =            0            ;            i            <            3            ;            i            ++            )            {            printf            (            "layer1[%d][%d]  Accost: %p  Value: %d            \n            "            ,            row            ,            i            ,            &            arr2            [            row            ][            i            ],            arr2            [            row            ][            i            ]);            }            printf            (            "            \n            "            );          

The output of this sequence follows:

arr2[0][0]  Address: 0x000100  Value: 0 arr2[0][1]  Address: 0x000104  Value: 1 arr2[0][2]  Address: 0x000108  Value: 2 arr2[0][three]  Accost: 0x000112  Value: 3  arr2[1][0]  Accost: 0x000116  Value: iv arr2[1][1]  Address: 0x000120  Value: v  arr2[ii][0]  Address: 0x000124  Value: 6 arr2[2][1]  Address: 0x000128  Value: seven arr2[2][2]  Address: 0x000132  Value: 8

Figure iv-20 depicts how memory is laid out for this assortment.

Jagged array memory allocation

Figure iv-xx. Jagged array memory allocation

In these examples, we used array notation as opposed to arrow notation when accessing the array'south contents. This made it somewhat easier to see and understand. Withal, arrow notation would have worked as well.

Compound literals are useful in creating jagged arrays. Nevertheless, accessing elements of a jagged assortment can be awkward, as demonstrated with the previous three for loops. This example tin can be simplified if a separate array is used to maintain the size of each column. While you can create jagged arrays in C, it may non be worth the effort.

Summary

We started with a quick review of arrays and and then examined the similarities and differences betwixt array and pointer note. Arrays can exist created using malloc blazon functions. These type of functions provide more flexibility than afforded by traditional assortment announcement. We saw how nosotros can utilize the realloc function to change the amount of memory allocated for an array.

Dynamically allocating retentivity for an array tin can nowadays challenges. In the case with ii or more dimensional arrays, we accept to be conscientious to brand certain the assortment is allocated in contiguous memory.

We also explored the problems that tin occur when passing and returning arrays. Passing the array'south size to a function is normally required and then the function tin can properly handle the array. We also examined how to create jagged arrays in C.

statenmajaus.blogspot.com

Source: https://www.oreilly.com/library/view/understanding-and-using/9781449344535/ch04.html

Post a Comment for "Program to Read a String From the User and Print It Using a Character Pointer Offset Notation"