How to Read a File and Store in Multidimensional Array in C#

Chapter 4. Pointers and Arrays

An array is a fundamental data structure built into C. A thorough understanding of arrays and their use is necessary to develop effective applications. Misunderstandings of assortment and pointer usage can result in difficult-to-notice errors and less than optimal operation in applications. Array and pointer notations are closely related to each other and can frequently be used interchangeably in the right context.

A common misconception is that an array and a pointer are completely interchangeable. An array proper name is not a pointer. Although an array name can be treated as a pointer at times, and array notation can be used with pointers, they are singled-out and cannot always be used in place of each other. Understanding this divergence will assist y'all avert wrong use of these notations. For case, although the name of an array used by itself will render the array's address, we cannot apply the name by itself every bit the target of an assignment.

Arrays support many parts of an application and tin exist 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 tin exist manipulated with pointers. You will see their apply in more advanced contexts throughout the book.

We showtime with a quick review of arrays and so examine the similarities and differences between array and pointer notation. Arrays can be created using malloc blazon functions. These functions provide more flexibility than that afforded past traditional array declarations. We will see how the realloc function can be used to alter the amount of memory allocated for an array.

Dynamically allocating memory for an assortment can present challenges, particularly when we are dealing with arrays with two or more dimensions, as we have to ensure that the array is allocated in contiguous memory.

We volition likewise explore problems that can occur when passing and returning arrays. In most situations, the array's size must be passed so the array can be properly handled in a function. There is nothing inherent in an assortment's internal representation that determines its length. If we do not pass the length, the part has no standard ways of knowing where the assortment ends. Nosotros will also examine how to create jagged arrays in C, although they are infrequently used. A jagged assortment is a 2-dimensional array where each row may take a different number of columns.

To demonstrate these concepts, we will use a vector for single-dimensional arrays and a matrix for two-dimensional arrays. Vectors and matrices have found extensive 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 be accessed using an index. By contiguous, we mean the elements of the array are adjacent to i another in memory with no gaps between them. By homogeneous, we mean they are all of the same type. Array declarations use a set of brackets and can possess multiple dimensions.

2-dimensional arrays are mutual, and we typically employ the terms rows and columns to depict the position of an array'due south element. Arrays with three or more dimensions are not as mutual simply tin be quite useful in some applications. A 2-dimensional array is not to exist confused with an array of pointers. They are like just bear slightly differently, every bit will be 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 office in the section Using the realloc Function to Resize an Array.

Note

Arrays have a fixed size. When we declare an assortment, we need to make up one's mind how big information technology should be. If we specify too many elements, we waste space. If we specify also few elements, we limit how many elements we can process. The realloc part and variable length arrays provide techniques for dealing with arrays whose size needs to change. With a petty piece of work, we can resize an array and use just the right corporeality of retention.

One-Dimensional Arrays

A one-dimensional array is a linear construction. It uses a unmarried index to access its members. The following is a declaration of a v-chemical element array of integers:

              int              vector              [              5              ];            

Assortment indexes start with 0 and end at 1 less than their declared size. Valid indexes for the array vector start at 0 and end at 4. Withal, C does not enforce these bounds. Using an invalid index for an array can result in unpredictable behavior. Effigy iv-i illustrates how the array is allocated in retentiveness. Each chemical element is four bytes in length and is uninitialized. Depending on the memory model used, as explained in Memory Models, the size may be dissimilar.

Array memory allocation

Figure 4-1. Array memory resource allotment

The internal representation of an array has no information about the number of elements it contains. The array name simply references a cake of retentiveness. Using the sizeof operator with an array volition return the number of bytes allocated to the array. To decide the number of elements, we dissever the array's size past its element's size, every bit illustrated below. This will display 5:

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

One-dimensional arrays can be readily initialized using a block blazon statement. In the post-obit sequence, each chemical element is initialized to an integer starting at one:

              int              vector              [              5              ]              =              {              one              ,              2              ,              3              ,              iv              ,              5              };            

Two-Dimensional Arrays

Two-dimensional arrays use rows and columns to identify array elements. This type of array needs to exist mapped to the 1-dimension address space of main memory. In C this is achieved by using a row-column ordering sequence. The array'southward offset row is placed in retentivity followed by the 2nd row, and then the 3rd row, and this ordering continues until the last row is placed in memory.

The following declares a two-dimensional array with two rows and three columns. The array is initialized using a cake statement. Figure 4-2 illustrates how retention 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              ][              3              ]              =              {{              ane              ,              2              ,              3              },{              iv              ,              five              ,              6              }};            

Two-dimensional array

Effigy 4-ii. 2-dimensional array

A 2-dimensional array is treated every bit an assortment of arrays. That is, when we access the array using merely one subscript, we become a pointer to the corresponding row. This is demonstrated in the following code sequence where each row'south accost and size is displayed:

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

The post-obit output assumes the array is located at address 100. The size is 12 because each row has three elements of four bytes each:

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

In the section Pointers and Multidimensional Arrays, we will examine this behavior in more detail.

Multidimensional Arrays

Multidimensional arrays accept two or more than dimensions. Equally with two-dimensional arrays, multiple sets of brackets ascertain the array'south blazon and size. In the following example, we ascertain a 3-dimensional array consisting of three rows, two columns, and a rank of four. The term rank is often used to announce the elements of the third dimension:

              int              arr3d              [              3              ][              2              ][              four              ]              =              {              {{              1              ,              2              ,              3              ,              iv              },              {              5              ,              6              ,              seven              ,              eight              }},              {{              ix              ,              10              ,              11              ,              12              },              {              xiii              ,              14              ,              xv              ,              sixteen              }},              {{              17              ,              18              ,              19              ,              20              },              {              21              ,              22              ,              23              ,              24              }}              };            

The elements are allocated contiguously in row-cavalcade-rank order equally illustrated in Figure four-3.

Three-dimensional array

Figure 4-3. Three-dimensional assortment

We will use these declarations in later examples.

Pointer Notation and Arrays

Pointers can be very useful when working with arrays. Nosotros tin use them with existing arrays or to allocate memory from the heap and so care for the retentivity as if information technology were an array. Array notation and arrow note tin be used somewhat interchangeably. However, they are not exactly the same as detailed in the department Differences Between Arrays and Pointers.

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

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

The variable pv is a pointer to the beginning element of the array and not the array itself. When nosotros kickoff assigned a value to pv, nosotros assigned the address of the array'due south get-go element.

We can utilize either the assortment proper noun by itself or utilize the address-of operator with the array's first element as illustrated beneath. These are equivalent and volition return the address of vector. Using the address-of operator is more verbose but too more explicit:

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

The expression &vector is sometimes used to obtain the address of an array. It differs from the other notations in that it returns a pointer to the entire array. The other ii approaches yield a arrow to an integer. Instead of returning a pointer to an integer, it returns a pointer to an array of integers. The utilise of this blazon volition exist illustrated in the section Passing a Multidimensional Array.

We can also utilise 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 subclass notation volition take the address contained in pv and adds the value contained in the index i using arrow arithmetic. This new address is and so dereferenced to render its contents.

As we discussed in the section Pointer Arithmetic, calculation an integer to a pointer will increment the address it holds by the product of the integer and the data blazon's size. The same is truthful if nosotros add an integer to the name of an array. The following two statements are equivalent:

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

Assume the vector is located at address 100 and pv is located at address 96. Table 4-one and Figure 4-4 illustrate the employ of assortment subscripts and pointer arithmetics with both the array name and the pointer for various values.

Table iv-1. Array/pointer notation

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

Array/pointer notation

Figure iv-4. Assortment/pointer notation

When we add one to the array address we effectively add 4, the size of an integer, to the accost since this is an array of integers. With the first and concluding operations, we addressed locations exterior the array's premises. While this is not a good exercise, it does emphasize the need to be conscientious when using indexes or pointers to access 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 pointer to the beginning of the array, shift two positions to the right, and then dereference that location to fetch its value. Using the accost-of operator in conjunction with array notation, as in &vector[2], substantially cancels out the dereferencing. It can be interpreted as go left 2 positions and and so return that accost.

The following demonstrates the utilize of pointers in the implementation of the scalar addition operation. This operation takes a value and multiplies it 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 utilize of arrays and the use of pointers to arrays. In this section, we volition use the vector array and pv arrow as defined below:

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

The code generated by vector[i] is dissimilar from the code generated by vector+i. The notation vector[i] generates auto code that starts at location vector, moves i positions from this location, and uses its content. The notation vector+i generates machine lawmaking 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 lawmaking is unlike. This difference is rarely of significance to nigh programmers.

At that place is a divergence when the sizeof operator is applied to an array and to a arrow to the same array. Applying the sizeof operator to vector will return xx, the number of bytes allocated to the array. Applying the sizeof operator confronting 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 be capable of beingness modified. An array proper name such every bit vector is not an lvalue and cannot be modified. The address assigned to an array cannot be inverse . A pointer tin be assigned a new value and reference a dissimilar section of retention.

Consider the post-obit:

              pv              =              pv              +              ane              ;              vector              =              vector              +              1              ;              // Syntax error            

We cannot modify vector, just its contents. However, the expression vector+i is fine, as demonstrated beneath:

              pv              =              vector              +              1              ;            

Using malloc to Create a One-Dimensional Array

If we allocate memory from the heap and assign the address to a pointer, there is no reason we cannot utilise array subscripts with the pointer and treat this memory as an array. In the following sequence, we indistinguishable the contents of the vector array used earlier:

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

We could have used pointer notation equally shown beneath; withal, the array notation is frequently easier to follow:

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

Effigy iv-5 illustrates how retention is allocated for this example.

Array allocated from the heap

Figure 4-five. Assortment allocated from the heap

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

Warning

In the previous example nosotros used *(pv+i) instead of *pv+ane . Since the dereference operator has college precedence than the plus operator, the 2nd expression's arrow is dereferenced, giving us the value referenced by the arrow. We 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 will complain. Thus, we need to strength the improver to be performed commencement, followed by the dereference operation, in order for it to work correctly.

Using the realloc Function to Resize an Assortment

Nosotros tin 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 bear witness to exist a better solution than using the realloc function. If y'all are non using C99, then the realloc function volition demand to be used. Besides, variable length arrays can only be declared every bit a member of a role. If the array is needed longer than the function's duration, then realloc will need to be used.

To illustrate the realloc role, we will implement a function to read in characters from standard input and assign them to a buffer. The buffer volition comprise all of the characters read in except for a terminating return character. Since we practice not know how many characters the user will input, nosotros practise not know how long the buffer should be. We will employ the realloc function to allocate additional space by a fixed increment corporeality. The lawmaking to implement this function is shown beneath:

            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            ==            Zippo            )            {            return            Cypher            ;            }            while            (            ane            )            {            character            =            fgetc            (            stdin            );            if            (            grapheme            ==            '\n'            )            {            break            ;            }            if            (            ++            length            >=            maximumLength            )            {            char            *            newBuffer            =            realloc            (            buffer            ,            maximumLength            +=            sizeIncrement            );            if            (            newBuffer            ==            Zero            )            {            free            (            buffer            );            return            Zero            ;            }            currentPosition            =            newBuffer            +            (            currentPosition            -            buffer            );            buffer            =            newBuffer            ;            }            *            currentPosition            ++            =            graphic symbol            ;            }            *            currentPosition            =            '\0'            ;            return            buffer            ;            }          

Nosotros will start past defining a serial of declarations as summarized in Table four-two.

Table four-2. getLine variables

sizeIncrement The size of the initial buffer and the corporeality it will exist incremented by when the buffer needs to exist enlarged
buffer A pointer to the characters read in
currentPosition A pointer to the adjacent free 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 grapheme read in

The buffer is created with a size of sizeIncrement. If the malloc function is unable to allocate retentiveness, the starting time if statement will strength the part to return Zippo. An space loop is entered where the characters are processed one at a time. When the loop exits, a NUL is added to stop the string and the buffer's accost is returned.

Within the while loop, a character is read in. If it is a carriage return, the loop is exited. Next, the if statement determines whether we have exceeded the buffer's size. Otherwise, the character is added to the current position inside the buffer.

If we have exceeded the buffer's size, the realloc function creates a new cake of memory. This block is sizeIncrement bytes larger than the old ane. If it is unable to classify memory, we free upwardly the existing allocated retention and force the function to return NULL. Otherwise, currentPosition is adapted to bespeak to the right position within the new buffer and we assign the variable buffer to signal to the newly allocated buffer. The realloc function will not necessarily go along your existing memory in place, and so you have to use the pointer it returns to figure out where your new, resized retentivity block is.

The variable newBuffer holds the allocated memory's address. We needed a separate variable, non buffer, in case the realloc was unable to allocate memory. This allows united states to detect and handle the status.

We did non free buffer if realloc was successful because realloc volition copy the original buffer to the new buffer and free up the quondam buffer. If nosotros had tried to free buffer, and then information technology would usually consequence in the program'southward termination because we tried to complimentary the aforementioned block of retentiveness twice.

Effigy iv-six illustrates memory beingness allocated for the getLine function with an input string of "Once upon a fourth dimension at that place was a giant pumpkin." The programme stack has been simplified to ignore the local variables except for buffer and currentPosition. The buffer has been extended four times, as indicated by the rectangle containing the input string.

Memory allocation for getLine function

Figure 4-6. Memory allocation for getLine function

The realloc role tin can also be used to decrease the amount of space used by a arrow. To illustrate its use, the trim function shown below will remove leading blanks in a string:

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

The outset while loop uses the tmp variable to skip over any leading blanks. The second while loop copies the remaining characters in the string to the commencement of the cord. It will evaluate to true until NUL is reached, which will evaluate to imitation. A naught is and then added to finish the string. The realloc function is and then used to reallocate the retentiveness based on the string's new length.

Effigy 4-7 illustrates the function'southward utilise with an original cord of "cat." The state of cord before and after the trim function executes is shown. The memory in crimson is the old retentiveness and should not be accessed.

Realloc example

Effigy 4-7. Realloc example

Passing a One-Dimensional Array

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

Unless there is something integral to the array to tell u.s. its bounds, we need to pass the size information when nosotros pass the array. In the instance of a string stored in an array, we can rely on the NUL termination character to tell u.s.a. when we can cease processing the array. We will examine this in Chapter 5. Generally, if we do not know the array'south size, we are unable to process its elements and tin can air current upwardly working with too few elements or treating memory exterior of the assortment every bit if it were function of the assortment. This volition frequently outcome in aberrant program termination.

We tin declare the array in the function declaration using 1 of ii notations: array notation or arrow note.

Using Array Notation

In the post-obit case, an integer array is passed to a role along with its size. Its contents are so displayed:

              void              displayArray              (              int              arr              [],              int              size              )              {              for              (              int              i              =              0              ;              i              <              size              ;              i              ++              )              {              printf              (              "%d              \n              "              ,              arr              [              i              ]);              }              }              int              vector              [              5              ]              =              {              one              ,              2              ,              3              ,              iv              ,              5              };              displayArray              (              vector              ,              5              );            

The sequence's output will be the numbers 1 through v.We passed the number 5 to the part that indicates its size. We could have passed any positive number and the function would attempt to display the respective number of elements, regardless of whether the size was correct. The program may terminate if we effort to address retentiveness outside of the array's bounds. The retention allocation for this example is shown in Figure iv-8.

Using array notation

Effigy 4-8. Using assortment note

Warning

A common mistake is to utilise the sizeof operator with the assortment in lodge to determine its number of elements, as shown below. Still, as explained in the sectionOne-Dimensional Arrays, this is not the correct way of determining its size. In this case, we would be passing the value of 20 to the assortment.

                displayArray                (                arr                ,                sizeof                (                arr                ));              

It is a common practice to pass a size smaller than the actual number of elements in an array. This is done to process just part of an assortment. For example, assume we read in a series of ages into an array but did not fill upwardly the array. If we called a sort function to sort it, we would only want to sort the valid ages, not every array chemical element.

Using Pointer Annotation

Nosotros practice not have to utilize the bracket note when declaring an array parameter of a office. Instead, we tin utilize arrow note equally follows:

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

We continued to use assortment notation within the office. If desired, nosotros could have used pointer notation in the function:

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

If nosotros had used assortment notation to declare the function, we could have nevertheless used pointer note in the role'south trunk:

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

Using a One-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 can also be found in:

  • Using an Array of Role Pointers, where we utilise an array of function pointers;

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

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

The purpose of this section is to prepare the stage for subsequently examples by illustrating the essence of the approach. The post-obit sequence declares an array of integer pointers, allocates memory for each chemical element, and initializes this memory to the array'southward index:

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

If this array was displayed, the numbers 0 through 4 would be printed. Nosotros used arr[i] to reference the pointer and *arr[i] to assign a value to the location referenced past the pointer. Exercise not let the apply of array notation confuse you. Since arr was alleged as an array of pointers, arr[i] returns an address. When we dereference a pointer such every bit *arr[i], we get the contents at that address.

We could take used the post-obit equivalent pointer notation for the loop's body:

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

This note is harder to follow, but understanding information technology will further your C expertise. We are using two levels of indirection in the second argument. Mastery of this blazon of annotation volition divide you from the less experienced C programmers.

The subexpression (arr+i) represents the address of the array's ith element. We need to change the content of this address so we use the subexpression *(arr+i). The allocated retentiveness is assigned to this location in the get-go statement. Dereferencing this subexpression a second fourth dimension, as we practise in the 2nd statement, returns the allocated memory's location. We then assign the variable i to it. Effigy 4-9 illustrates how retentiveness is allocated.

For example, arr[1] is located at address 104. The expression (arr+ane) will give us 104. Using *(arr+1) gives united states its content. In this example, it is the pointer 504. Dereferencing this a 2nd fourth dimension using **(arr+ane) gives us the contents of 504, which is a ane.

Array of pointers

Figure four-9. Array of pointers

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

Table 4-3. Array of pointers expressions

Expression Value
*arr[0] 0
**arr 0
**(arr+i) ane
arr[0][0] 0
arr[iii][0] 3

The get-go three expressions are like to those in the previous caption. The final two are unlike. The use of a pointer to a pointer notation suggests we are dealing with an array of pointers. In outcome, this is what nosotros are doing. If we reexamine Effigy 4-ix and pretend each element of arr points to an array of size one, and then the last two expressions make sense. What we have is a five-chemical element array of pointers to a series of one-element arrays.

The expression arr[3][0] refers to the fourth element of arr and so the kickoff element of the array it points to. The expression arr[iii][1] does not piece of work because the array the 4th element is pointing to does not have 2 elements.

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

Pointers and Multidimensional Arrays

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

To illustrate this behavior, nosotros create a two-dimensional array and initialize information technology as follows:

            int            matrix            [            2            ][            5            ]            =            {{            one            ,            2            ,            3            ,            four            ,            five            },{            6            ,            7            ,            8            ,            9            ,            x            }};          

The addresses and their respective values are then displayed:

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

The output follows:

matrix[0][0]  Address: 100  Value: 1 matrix[0][one]  Address: 104  Value: ii matrix[0][2]  Address: 108  Value: 3 matrix[0][3]  Address: 112  Value: 4 matrix[0][4]  Address: 116  Value: five matrix[1][0]  Address: 120  Value: 6 matrix[1][1]  Address: 124  Value: seven matrix[1][ii]  Accost: 128  Value: 8 matrix[1][iii]  Address: 132  Value: 9 matrix[1][4]  Address: 136  Value: 10

The array is stored in row-column order. That is, the first row is stored sequentially in memory followed by the 2d row. The memory resource allotment is illustrated in Figure 4-ten.

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

            int            (            *            pmatrix            )[            v            ]            =            matrix            ;          

Two-dimensional array memory allocation

Effigy 4-10. Two-dimensional array memory allocation

The expression, (*pmatrix), declares a pointer to an array. Combined with the rest of the declaration, pmatrix is defined as a pointer to a two-dimensional assortment of integers with five elements per column. If we had left the parentheses off, we would accept declared a five-element array 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 array, then the results are unpredictable.

If we want to access the second element, ii, using pointer notation, it might seem reasonable to use the following:

            printf            (            "%p            \northward            "            ,            matrix            );            printf            (            "%p            \n            "            ,            matrix            +            i            );          

The output follows:

100 120

The address returned by matrix+1 is not starting time by 4 from the commencement of the array. Instead, information technology is offset by the first row's size, 20 bytes. Using matrix by itself returns the address of the array's first element. Since a two-dimensional array is an array of arrays, we go the address of a five-element integer assortment. Its size is twenty. We can verify this with the following statement, which will display 20:

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

To access the assortment's 2nd element, nosotros need to add together 1 to the first row of the array equally follows: *(matrix[0] + 1). The expression, matrix[0], returns the accost of the starting time element of the beginning row of the array. This accost is the address of an array of integers. Thus, when nosotros add one to information technology, the size of a single integer is added to it, giving u.s.a. the 2nd element. The output volition be 104 and 2.

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

We can graphically describe the assortment as illustrated in Effigy iv-11.

Graphically depiction of a two-dimensional array

Figure 4-xi. Graphically delineation of a ii-dimensional assortment

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

Two-dimensional array notation

Figure four-12. Two-dimensional array notation

Passing a Multidimensional Assortment

Passing a multidimensional array to a function can be disruptive, specially when pointer notation is used. When passing a multidimensional assortment, we demand to determine whether to use array note or pointer notation in the function'south signature. Another consideration is how to convey the array'south shape. By shape, we are referring to the number and size of its dimensions. If we want to use array annotation inside the function, it is imperative to specify the array'southward shape. Otherwise, the compiler is unable to utilize subscripts.

To pass the matrix array, apply 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 data is not passed, then it is unable to evaluate expressions such equally arr[0][3] as explained in the section Pointers and Multidimensional Arrays.

In the start version, the expression arr[] is an implicit declaration of a arrow to an array. In the second version, the expression (*arr) is an explicit declaration of the pointer.

Alert

The following annunciation volition not work correctly:

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

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

A simple implementation of this function 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            (            "            \north            "            );            }            }            void            main            ()            {            int            matrix            [            two            ][            5            ]            =            {            {            1            ,            two            ,            3            ,            4            ,            5            },            {            vi            ,            7            ,            8            ,            9            ,            10            }            };            display2DArray            (            matrix            ,            2            );            }          

The function does not allocate retentiveness for the array. But the address is passed. The program stack's state for this phone call is shown in Figure 4-thirteen.

Passing multidimensional array

Figure 4-xiii. Passing multidimensional assortment

You lot may run into a function declared as follows. It is passed a single pointer 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            (            "            \northward            "            );            }            }          

The printf statement calculates the address of each element by adding to arr the number of elements in the previous row(s), (i*cols), and so adding j to specify the column. To invoke the function, we can use the following:

            display2DArrayUnknownSize            (            &            matrix            [            0            ][            0            ],            2            ,            v            );          

Within the function, we cannot utilize array subscripts as shown below:

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

This is not possible because the pointer is non declared equally a ii-dimensional array. Nevertheless, information technology is possible to use array notation as shown below. We tin utilize a unmarried subscript since information technology will be interpreted but as an offset within the array, whereas ii subscripts cannot be used because the compiler doesn't know the size of the dimensions:

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

The beginning element's address is passed using &matrix[0][0] instead of matrix. While using matrix volition execute correctly, a alarm will be generated, indicating incompatible pointer types. The expression &matrix[0][0] is a pointer to an integer, whereas matrix is a pointer to an array of integers.

When passing an array with more than two dimensions, all just the size of the start dimension need to be specified. The following demonstrates a part written to display a three-dimensional assortment. The last two dimensions are specified in the annunciation:

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

The following code shows the function'south invocation:

            int            arr3d            [            3            ][            2            ][            4            ]            =            {            {{            1            ,            two            ,            3            ,            iv            },            {            five            ,            6            ,            7            ,            8            }},            {{            9            ,            ten            ,            11            ,            12            },            {            13            ,            14            ,            15            ,            16            }},            {{            17            ,            18            ,            19            ,            20            },            {            21            ,            22            ,            23            ,            24            }}            };            display3DArray            (            arr3d            ,            3            );          

The output follows:

{one two 3 4 }{five 6 vii 8 } {ix 10 11 12 }{13 14 xv 16 } {17 eighteen 19 20 }{21 22 23 24 }

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

Three-dimensional array

Effigy 4-14. Three-dimensional assortment

The expression arr3d[i] refers to the array's second row and is a arrow to a ii-dimensional assortment with two rows and four columns. The expression arr3d[ane][0] refers to the second row, first cavalcade of the assortment and is a pointer to a ane-dimensional array of size v.

Dynamically Allocating a Two-Dimensional Array

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

  • Whether the array elements need to be contiguous

  • Whether the assortment is jagged

Retentivity is allocated contiguously when a two-dimensional array is declared every bit follows:

            int            matrix            [            2            ][            5            ]            =            {{            one            ,            ii            ,            3            ,            four            ,            5            },{            6            ,            7            ,            8            ,            ix            ,            x            }};          

Even so, when we use a part such as malloc to create a two-dimensional assortment, in that location are variations in how memory can be allocated. Since a two-dimensional array tin can be treated as an array of arrays, there is no reason the "inner" arrays need to be face-to-face. When assortment subscripts are used with such an assortment, the array's noncontiguous nature is handled transparently.

Notation

Whether or non information technology is face-to-face can bear on other operations, such as copying a block of retention. Multiple copies may exist required if the retentiveness is not contiguous.

Allocating Potentially Noncontiguous Memory

The following illustrates i style of allocating a two-dimensional assortment where the allocated memory is not guaranteed to be contiguous. Starting time, the "outer" array is allocated and then each row is allocated using separate malloc statements:

              int              rows              =              2              ;              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 separate malloc calls were used, the allocated retention is not guaranteed to exist contiguous. This is illustrated in Figure 4-15.

Noncontiguous allocation

Figure 4-15. Noncontiguous allocation

The actual allocation depends on the heap manager and the heap's state. Information technology may well be contiguous.

Allocating Contiguous Retention

We volition present two approaches for allocating contiguous memory for a 2-dimensional assortment. The showtime technique allocates the "outer" array commencement and so all of the retentiveness for the rows. The 2nd technique allocates all of the retentiveness at once.

The first technique is illustrated in the following sequence. The offset malloc allocates an assortment of pointers to integers. Each element will exist used to hold a pointer to a row. This is the block allocated at accost 500 in Figure 4-16. The second malloc allocates memory for all of the elements of the array at location 600. In the for loop, each element of the first array is assigned a portion of the memory allocated by the 2nd chiliad alloc:

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

Contiguous allocation with two malloc calls

Figure four-sixteen. Contiguous allocation with 2 malloc calls

Technically, the retention for the commencement array may be separated from the memory for the array'south "body." Nonetheless, a contiguous region of memory is allocated for the body.

In the second technique shown beneath, all of the memory for the assortment is allocated at i time:

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

This allocation is illustrated in Figure 4-17.

Contiguous allocation with a single malloc call

Figure 4-17. Face-to-face allocation with a single malloc telephone call

When the array is referenced after in lawmaking, assortment subscripts cannot exist used. Instead, indexes into the array need to be calculated manually, as illustrated in the following code sequence. Each array chemical 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 accept lost the shape information needed by the compiler to permit subscripts. This concept is explained in the department Passing a Multidimensional Array.

This approach has limited utilize in the existent world, simply it does illustrate the relationship between the concept of a two-dimensional assortment and the one-dimensional nature of main retention. The more user-friendly two-dimensional array notation makes this mapping transparent and easier to utilise.

We take demonstrated 2 general approaches for allocating contiguous memory for a two-dimensional array. The arroyo to use depends on the needs of the application. Even so, the last approach generates a single block of memory for the "entire" array.

Jagged Arrays and Pointers

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

Jagged array

Figure iv-eighteen. Jagged array

Before we acquire how to create such an assortment, let'due south examine a two-dimensional array created using compound literals. A chemical compound literal is a C construct that consists of what appears to be a cast operator followed by an initializer list enclosed in braces. An example of a chemical compound literal follows for both a abiding integer and an array of integers. These would be used as part of a proclamation:

            (            const            int            )            {            100            }            (            int            [            iii            ])            {            x            ,            twenty            ,            30            }          

In the following declaration, we create the array arr1 past declaring it as an assortment of pointers to an integer and using a block argument of chemical compound literals to initialize it:

            int            (            *            (            arr1            []))            =            {            (            int            [])            {            0            ,            ane            ,            two            },            (            int            [])            {            3            ,            four            ,            5            },            (            int            [])            {            vi            ,            vii            ,            8            }};          

This assortment has three rows and three columns. The assortment'south elements are initialized with the value 0 through eight in row column lodge. Figure four-19 depicts how memory is laid out for this array.

Two-dimensional array

Effigy 4-19. Two-dimensional array

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

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

When executed, nosotros volition get the following output:

arr1[0][0]  Address: 0x100  Value: 0 arr1[0][1]  Address: 0x104  Value: 1 arr1[0][two]  Address: 0x108  Value: ii  arr1[1][0]  Address: 0x112  Value: 3 arr1[1][1]  Address: 0x116  Value: iv arr1[1][2]  Accost: 0x120  Value: five  arr1[2][0]  Accost: 0x124  Value: 6 arr1[2][i]  Accost: 0x128  Value: 7 arr1[two][2]  Address: 0x132  Value: viii

This declaration can exist modified slightly to create a jagged array as depicted in Figure 4-xviii. The array proclamation follows:

            int            (            *            (            arr2            []))            =            {            (            int            [])            {            0            ,            1            ,            2            ,            3            },            (            int            [])            {            4            ,            5            },            (            int            [])            {            6            ,            seven            ,            viii            }};          

We used three compound literals to declare the jagged array. The array's elements are initialized in row-column order starting with a value of aught. 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            (            "            \north            "            );            row            =            1            ;            for            (            int            i            =            0            ;            i            <            ii            ;            i            ++            )            {            printf            (            "layer1[%d][%d]  Address: %p  Value: %d            \northward            "            ,            row            ,            i            ,            &            arr2            [            row            ][            i            ],            arr2            [            row            ][            i            ]);            }            printf            (            "            \n            "            );            row            =            two            ;            for            (            int            i            =            0            ;            i            <            three            ;            i            ++            )            {            printf            (            "layer1[%d][%d]  Address: %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: one arr2[0][ii]  Accost: 0x000108  Value: 2 arr2[0][3]  Address: 0x000112  Value: 3  arr2[one][0]  Address: 0x000116  Value: four arr2[1][1]  Address: 0x000120  Value: v  arr2[2][0]  Address: 0x000124  Value: 6 arr2[2][one]  Address: 0x000128  Value: 7 arr2[2][ii]  Address: 0x000132  Value: eight

Figure 4-xx depicts how memory is laid out for this array.

Jagged array memory allocation

Figure iv-xx. Jagged array memory allotment

In these examples, nosotros used array annotation as opposed to arrow notation when accessing the array's contents. This fabricated it somewhat easier to see and understand. Withal, pointer annotation would have worked as well.

Compound literals are useful in creating jagged arrays. However, accessing elements of a jagged array can be awkward, every bit demonstrated with the previous 3 for loops. This example can be simplified if a separate assortment is used to maintain the size of each column. While y'all tin create jagged arrays in C, it may not be worth the effort.

Summary

We started with a quick review of arrays and and then examined the similarities and differences between array and arrow notation. Arrays tin be created using malloc type functions. These type of functions provide more flexibility than afforded past traditional array declaration. We saw how we can use the realloc role to alter the corporeality of memory allocated for an array.

Dynamically allocating retentivity for an array can present challenges. In the case with 2 or more dimensional arrays, nosotros have to be conscientious to make sure the array is allocated in contiguous memory.

We likewise explored the issues that can occur when passing and returning arrays. Passing the assortment's size to a part is usually required so the part tin can properly handle the array. Nosotros too examined how to create jagged arrays in C.

laraanimad.blogspot.com

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

0 Response to "How to Read a File and Store in Multidimensional Array in C#"

Postar um comentário

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel