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:
intvector[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.
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:
intvector[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:
intmatrix[2][3]={{ane,2,3},{iv,five,6}};
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(inti=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:
intarr3d[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.
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:
intvector[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 |
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;intvalue=3;for(inti=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:
intvector[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(inti=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(inti=0;i<5;i++){*(pv+i)=i+i;}
Effigy iv-5 illustrates how retention is allocated for this example.
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 instead of*(pv+i) . 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 *pv+ane 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){constsize_tsizeIncrement=x;char*buffer=malloc(sizeIncrement);char*currentPosition=buffer;size_tmaximumLength=sizeIncrement;size_tlength=0;intcharacter;if(currentPosition==Zippo){returnCypher;}while(ane){character=fgetc(stdin);if(grapheme=='\n'){break;}if(++length>=maximumLength){char*newBuffer=realloc(buffer,maximumLength+=sizeIncrement);if(newBuffer==Zero){free(buffer);returnZero;}currentPosition=newBuffer+(currentPosition-buffer);buffer=newBuffer;}*currentPosition++=graphic symbol;}*currentPosition='\0';returnbuffer;}
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 function is unable to allocate retentiveness, the starting time mallocif 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.
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);}intmain(){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.
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:
voiddisplayArray(intarr[],intsize){for(inti=0;i<size;i++){printf("%d\n",arr[i]);}}intvector[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.
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:
voiddisplayArray(int*arr,intsize){for(inti=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:
voiddisplayArray(int*arr,intsize){for(inti=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:
voiddisplayArray(intarr[],intsize){for(inti=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
argvarray 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(inti=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.
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:
intmatrix[2][5]={{one,2,3,four,five},{6,7,8,9,x}};
The addresses and their respective values are then displayed:
for(inti=0;i<2;i++){for(intj=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;
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.
Figure 4-xi. Graphically delineation of a ii-dimensional assortment
Two-dimensional array notation can be interpreted every bit shown in Figure 4-12.
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:
voiddisplay2DArray(intarr[][5],introws){
or:
voiddisplay2DArray(int(*arr)[five],introws){
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:
voiddisplay2DArray(int*arr[five],introws){
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:
voiddisplay2DArray(intarr[][5],introws){for(inti=0;i<rows;i++){for(intj=0;j<5;j++){printf("%d",arr[i][j]);}printf("\north");}}voidmain(){intmatrix[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.
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:
voiddisplay2DArrayUnknownSize(int*arr,introws,intcols){for(inti=0;i<rows;i++){for(intj=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:
voiddisplay3DArray(int(*arr)[2][4],introws){for(inti=0;i<rows;i++){for(intj=0;j<2;j++){printf("{");for(intk=0;g<4;one thousand++){printf("%d ",arr[i][j][yard]);}printf("}");}printf("\n");}}
The following code shows the function'south invocation:
intarr3d[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.
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:
intmatrix[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:
introws=2;intcolumns=5;int**matrix=(int**)malloc(rows*sizeof(int*));for(inti=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.
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:
introws=2;intcolumns=5;int**matrix=(int**)malloc(rows*sizeof(int*));matrix[0]=(int*)malloc(rows*columns*sizeof(int));for(inti=1;i<rows;i++)matrix[i]=matrix[0]+i*columns;
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.
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(inti=0;i<rows;i++){for(intj=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.
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:
(constint){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.
Effigy 4-19. Two-dimensional array
The following sequence displays the addresses and values of each assortment element:
for(intj=0;j<3;j++){for(inti=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:
introw=0;for(inti=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(inti=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(inti=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.
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.
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