Arrays and Pointers Lecture 15 Narrator: Lecture 15: Arrays and Pointers Winter Quarter
Arrays and Pointers So far, we have looked at pointer variables which were declared with statements like: FILE *fptr ; int *aptr ; We have also used pointer constants when we sent the address of a variable to a function. We did this by placing an ampersand before the variable name. For instance, the address of variable a is &a, which is a pointer constant. Instructor: Thus far the lectures have combined pointers and single variables. This lecture discusses the use of pointer with arrays and their usefulness when doing so. It begins with a recap of pointers in previous lectures. This lecture contains more complicated address arithmetic and less straightforward code than the previous lectures. Invite students to ask many questions since this proves to be one of the more difficult lectures. Narrator: Thus far the lectures have combined pointers and single variables. This lecture discusses the use of pointer with arrays and their usefulness when doing so. Recently we have looked at pointer variables which were declared much like the lines shown here. We have also used pointer constants when we sent the address of a variable to a function. We did this by placing an ampersand before the variable name. For example, the address of the variable a is &a, which is a pointer constant. Winter Quarter
Arrays and Pointers The name of an array without a subscript (index number) is also a pointer constant. When the name is used without the subscript it references the address of element 0 of the array. int myarray[10]; /* Declare an array */ int *myptr; /* Declare a pointer (not initialized)*/ printf (“%d\n”, myarray); /* print address of myarray[0] */ scanf (“%d”, myarray); /* get value from keyboard and store in myarray[0] */ scanf (“%d”,&myarray[0]); /* same thing as above */ Instructor: An array already has a pre-defined “pointer variable” assigned to it, which is basically the array name without an index. This slide provides code which demonstrates multiple ways to resolve the address of myarray[0]. Narrator: The name of an array without a subscript, or index, is also a pointer constant. When the name of a single-dimension array is used without the subscript it references the address of where the array begins, which is also element 0 of the array. The code shown here demonstrates how to acquire and use the address within common functions. The final line completes the same command as the line above, returning the address of the first element of myarray. Winter Quarter
Arrays and Pointers myptr = &myarray[2]; /* Assign the address of the third element of myarray to myptr */ printf(“%d”, *myptr); /* Print the value of what myptr is pointing to, i.e., the value of myarray[2] */ Note that the * in front of myptr de-references the pointer. That is, it says “use the value in the address that is pointed to.” The following shows a small program and its output. Instructor/Narrator: Like with single variables, one may return the address of any variable with the &. In this case the third element’s address in myarray is assigned to a pointer myptr. Just like in previous lectures, the * dereferences the pointer, meaning take the value stored in myptr as an address, go to that address and return the value stored in that address. Winter Quarter
Arrays and Pointers /* Printing array values and addresses */ #include <stdio.h> int main ( ) { int k; float a[4] = {1000, 2000, 3000, 4000}; printf ("k a &a[k] a[k]\n"); for (k=0; k<4; k++) printf ("%d %ld %ld %f\n", k, a, &a[k], a[k]); } Instructor/Narrator: This code demonstrates the effects of using various operators on variables and arrays. The four variables that will be printed to the screen are: k: the index, which will be incremented from 0 to 3. a: an array of floating point values. Remember the name of an array by itself will return a pointer to the first address of the array. &a[k]: returns the address of the kth element of the array a a[k]: returns the value stored in the kth element of the array a An example output is on the following slide. Winter Quarter
Running the Program r1tel (~) millerm 52> a.out k a &a[k] a[k] 0 2147462916 2147462916 1000.000000 1 2147462916 2147462920 2000.000000 2 2147462916 2147462924 3000.000000 3 2147462916 2147462928 4000.000000 r1tel (~) millerm 53> Instructor/Narrator: As expected, the k, and a[k] columns contain the increasing index and the values stored in the array respectively. The result of using the variable a returns the address of the beginning of the array always. The result of &a[k] returns the address of each element in the array. Notice the first row a and &a[0] return the same value since each is essentially returning the same thing. The &a[k] row increases by 4 for each index since the array is of type float, which requires 4 bytes for each value stored in the array. Remember, for each execution the numbers representing addresses in this slide will vary with each execution of the program depending on what memory the operating system reserves for this program. Winter Quarter
Arrays and Pointers We have seen how to pass a pointer for a single valued variable to a function. Sometimes we want to pass an entire array to a function. The name of an array without a subscript is a pointer constant that contains the address of element [ 0 ] of the array. Therefore, if we pass the array name with no subscript to a function, we are passing a pointer to that array. The following program illustrates this. Instructor: Again, emphasizing the ability to pass a pointer to an array using simply the array’s name without an index. Narrator: We have previously seen how to pass a pointer for a single-valued variable to a function, but sometimes we want to pass an entire array to a function. We also know that the name of an array without a subscript is a pointer constant, that contains the address of element 0 of the array. Therefore, if we pass the array name with no subscript to a function, we are passing a pointer to that array. This next program illustrates this. Winter Quarter
Arrays and Pointers /* Passing an array to a function */ #include <stdio.h> #include <string.h> void myfunct (int , char [ ]); int main ( ) { char name[20] = "Michael J. Miller"; myfunct (20, name); } Instructor/Narrator: This code demonstrates passing of an array to a user-written function via a pointer. Notice in the function prototype the array size doesn’t need to be defined. The compiler simply needs to know that a array pointer will be passed in this argument which is done as shown. Our array is a character array in this case, initialized with the string as shown. The function myfunct() then requires as arguments the size of the array, and the pointer to the array. Winter Quarter
Arrays and Pointers void myfunct (int len , char text[ ]) { int k ; printf ("%d\n", strlen(text)) ; for (k = 0 ; k < len ; k++) printf ("%c", text[k]) ; } /*Program Output */ 17 Michael J. Miller Instructor: The user-written function handles the printing of the values in the array defined in the main() function. The function strlen() determines the length of a string, or array of characters. More string-handling functions will be discussed in the next lecture. We are concerned here with how the function takes as input the array pointer, now with the name text. Notice with arrays you can use the pointer as it was passed to the function, and when you wish to acquire a value in the array you simple place the index after the name again, just like in the main() function that declared the array. Winter Quarter
Arrays and Pointers Given the declaration int a[3] = { 1, 2, 3 } ; a is a pointer to (the address of) a[0] &a[0] is a pointer to a[0] a[0] is the value 1 ( *a is also the value 1) &a[1] is a pointer to a[1] a[1] is the value 2 &a[2] is a pointer to a[2] a[2] is the value 3 &a[0]+1 is a pointer to a[1] &a[0]+2 is a pointer to a[2] Instructor: This slide summarizes the various ways to obtain the address and data of each element of an array. Students may wish to print a copy of this slide and use it as a reference until they feel comfortable with using pointers and arrays. Narrator: This slide summarizes the various ways to obtain the address and data of each element of an array. You may wish to print a copy of this slide and use it as a reference until you feel comfortable using pointers and arrays. The array name by itself is a pointer to the beginning of the array, which is also the address of the first element. &a[0] is also the same thing. a[0] returns the value stored in the first element of the array, and so on. You can also perform math with these pointers as shown in the last two lines. Winter Quarter
Arrays and Pointers Given the declaration int b[3][3] = {{1,3,5}, {7,9,11}, {13,15,17}}; b is a pointer to b[0][0] b[0] is also a pointer to b[0][0] b[1] is a pointer to b[1][0] b[2] is a pointer to b[2][0] *b is a pointer to b[0] (special case) *b[1] is the value of b[1][0] (which is 7) *b[2] + 1 is the value of b[2][0] + 1 (which is 14) Instructor/Narrator: This slide demonstrates multiple ways to obtain pointers and values of a 2-dimensional array. When determining the index of a multidimensional array it is easiest to think of declaration as an array of arrays (which can be easily visualized with the given declaration). The indices then start at the outermost array and work inwards. The first index decides which inner array is selected. The next index in this example decides which value of the chosen inner array is selected. When one of the indices is left off of an array variable, this also becomes a pointer to the first element of the array chosen by the given indices as demonstrated in the first few examples. These pointers can be dereferenced (*) like any other pointer to return the value stored in that location. A special case exists when the entire multidimensional array is dereferenced. You may also wish to print this page as a reference, but programs in this class will generally not require the use of multidimensional arrays and pointers. Winter Quarter
/* Double subscript arrays and user-written functions */ #include <stdio.h> void printarray (int [ ][7], int); int main( ) { int calendar[5][7]={{1,2,3,4,5,6,7}, {8, 9, 10,11,12,13,14}, {15,16,17,18,19,20,21}, {22,23,24,25,26,27,28}, {29,30,31,32,33,34,35}} ; printarray (calendar , 5); } Instructor/Narrator: This program creates a “calendar” of values in a two-dimensional array. To keep things simple we overload the calendar with dates that actually don’t exist and day 1 of the calendar begins in the first element of the array. A user-written function printarray() requires a pointer to the calendar array and the number of weeks in the calendar, in this case 5. Notice in the function prototype since we assume to always be dealing with a calendar, the second index is set statically to 7, since 7 days always exist in a week. The user-written function on the next slide does the real work of this program. Winter Quarter
void printarray (int cal[][7], int j) { int k, n ; for (k = 0 ; k < j ; k++) for (n = 0 ; n < 7 ; n++) printf ("%3d ", cal[k][n]); printf ("\n"); } Instructor/Narrator: Nested for() loops cycle through the week index in the outer loop, and the day index in the inner loop. The order of these nested loops is important since if they were reversed the program would first print out every Sunday, then every Monday, etc. (the day index would remain static while the inner loop increments the week variable). After each week is printed with the inner for() loop and single printf(), a new line character is printed with the second printf() after the inner for() loop terminates (notice no braces around the commands in the second for() loop, so only the first printf() is part of this loop). The printf() which prints the days contains the array cal with its indices. Notice that since the variable cal with no indices is a pointer, and including all the indices to this array returns a value, there is no need to dereference a pointer with *. Winter Quarter
/* Double subscript arrays, user-written functions and pointer arithmetic */ #include <stdio.h> void printarray (int * , int , int); int main ( ) { int calendar[5][7]= {{1,2,3,4,5,6,7}, {8, 9, 10,11,12,13,14}, {15,16,17,18,19,20,21}, {22,23,24,25,26,27,28}, {29,30,31,32,33,34,35}}; printarray (calendar[0] , 5 , 7); } Instructor/Narrator: Another (more complicated) method is shown in this example which produces the same results using methods that would normally be used for single-element variables. Notice the function prototype now requires a integer pointer and both array “size” variables. The pointer to the first element of the calendar array is now passed as an individual variable pointer. Winter Quarter
void printarray (int *cal, int j, int m) { for (k = 0 ; k < j*m ; k += m) for (n = 0 ; n < m ; n++) printf ("%3d ", *(cal+k+n)); printf ("\n"); } Instructor: Drawing out the array and a column of memory and demonstrating how it is stored may serve useful for students. Narrator: Since elements of an array are stored sequentially (the same order they were declared on the previous slide) one can use a pointer to the first element of the array as a base, and then calculate an offset based on the dimensions of the array to acquire the value the programmer wishes. In this case the code steps through every element of the array in order by adding an integer value to the base from 0 to the (j*m-1)th element and then dereferencing this new address to obtain the value. Winter Quarter
Assignment E13 A data file named E13.dat exists in the class "common area" on the UNIX system. The file contains actual data from one test of an instrumented bicycle from an engineering hands-on lab experiment. You should look at the file on the screen (with a "more" command), but DO NOT print it out, as it contains several thousand (but less than 12,000) lines of data. Instructor: If you are not sure of the “common area” for your class you may ask another instructor or speak with someone in HI317. Narrator: In assignment 13 you will need access to a data file named E13.dat which is in the class’s common area. This file contains actual data from one of the tests of an instrumented bicycle fron an engineering hands-on lab experiment. Copy this file to your directory and open the file on the screen with the “more” command. DO NOT print it out though, since it contains several thousand lines of data. Winter Quarter
Assignment E13 You will note that at the beginning of the file there are several lines of "header" information followed by many lines of data in four columns. Count by hand the number of lines from the beginning of the file until you get to a line that has the actual data in the four columns. (You will need this number in Step 2 later.) The fourth column is the raw data (voltage) values from the lab experiment. Instructor: The students count the number of header lines in the file since we have yet to establish a good method for ruling out which lines may be headers and which may contain actual data. Students who finish early or wish to have more of a challenge may wish to use a more advanced method for filtering out this header information. Narrator: When you open this file you will notice before the data there are a few lines of text, or header information. You can count by hand the number of lines from the beginning of the file, including blank lines until the beginning of data. This will become handy in step 2. In the actual data, column 4 is raw voltage values from the lab experiment. Winter Quarter
Steps for E13 Write a complete C program, (say, E13.cpp), which does the following: Opens the data file for input. Input the correct number of header lines one by one, display each one on the screen and print each one to a result file (say, d13res.dat), and then discard the information. Narrator: Your program should be able to open the data file for input, input the correct number of header lines one by one, display them on the screen and output them to a file, and then discard this header information. Winter Quarter
Steps for E13 For opening the data file and the output file, just use the usual fopen routine. For reading the header lines, it might be rather convenient to read the complete line into a character array or string. How long should this string be? What routine might be used? Instructor: The students can use fgets() to get an entire line from their file. Some useful code: char tempstring[100]; FILE *infile; //open file, etc. fgets(tempstring,100,infile); SYNOPSIS #include <stdio.h> char *fgets(char *s, size_t n, FILE *stream); DESCRIPTION The fgets() function reads at most n-1 characters from stream into the buffer pointed to by s. No additional characters are read after fgets() has read and transferred a newline character to the buffer. A null character is written immediately after the last character that fgets() reads into the buffer. PARAMETERS s Points to a buffer that is to hold the characters read. n Is the size, in bytes, of the buffer pointed to by s. stream Is the stream from which the characters are read. Narrator: To open the file, simply use the fopen routine. For reading the header lines, it would be read each line as a complete string rather than each individual character. A routine that might be useful is the fgets() command. Check your “C How To Program” for more information on this command. Also you must be concerned that your character array is large enough to hold a whole line of characters. One way would be to count the number of characters in the longest line in the header file, or to make it ANSI C compliant you can simply make your array at least 256 characters large. Winter Quarter
Steps for E13 Input each of the lines of data arranged in the four columns, discarding the data values from each of the first three columns and storing only the data from the fourth column in a one-dimensional array. For skipping over the columns with unwanted data, you will need to use the assignment suppression operator, *, in the scanf format. Your program will need to detect the end-of-file (EOF) to know when to stop inputting data. Close the input file when you reach the EOF. Instructor: An example of the suppression operator is given on slide 23. Students should remember how to check for EOF after using fscanf using either feof() or a logical test for EOF. Narrator: Now that we have reached the lines of data, we must discard the first three columns of data, and store the data from the fourth column in a one-dimensional array. Again we must make sure this array will be large enough to store all the data. One way you could throw away data is to actually store it in a variable, but then not do anything with it. A more useful way that doesn’t waste memory is to ignore or suppress this data, which you can do in the scanf format with the suppression operator, which again is the asterisk. Your program must also need to detect the EOF condition. Close the input file when you reach the EOF. Winter Quarter
Data from e13.dat " "Source File: C:\PROGRA~1\PSLOG\195RIDE.PL1 "ID: IE Group 4 Bike Stress friday XR440 12 bi "Ch1 lbl/scl: Start/Stop /A "Ch2 lbl/scl: input voltage signal /-5.000 05.000 "Ch3 lbl/scl: Off /C "Ch4 lbl/scl: Off /C "Rate (mins): 0.0000833333324 Bat: 8.8 "First: Fri 20-Nov-1998 10:38:47am "Last: Fri 20-Nov-1998 10:39:40am "Transferred: Fri 20-Nov-1998 10:42:37am "Eq. PC time: Fri 20-Nov-1998 10:42:38AM Instructor: Example data is shown here. This portion would be a header. Narrator: The data shown here is example header information. This portion will be displayed by your program, stored to the output file, and then discarded. Winter Quarter
Data from e13.dat "Rate (mins): 0.0000833333324 Bat: 8.8 "First: Fri 20-Nov-1998 10:38:47am "Last: Fri 20-Nov-1998 10:39:40am "Transferred: Fri 20-Nov-1998 10:42:37am "Eq. PC time: Fri 20-Nov-1998 10:42:38AM " Date,Time,Ch1:Deg F,Ch2: 11/20/1998 10:38:47.000 -4.989 0.238 11/20/1998 10:38:47.005 -4.989 0.231 11/20/1998 10:38:47.010 -4.989 0.228 11/20/1998 10:38:47.015 -4.989 0.231 11/20/1998 10:38:47.020 -4.989 0.228 Instructor: Data begins after the comma-separated column headers. Students want to keep only the last column of data. Narrator: The rest of a header is shown and then the data begins AFTER the comma-separated column headers. The column headers can be considered part of the header. Your program will keep only the last column of data. Winter Quarter
Processing the E13 Data File For skipping unwanted columns, we need to use the "assignment suppression operator" in the format specification: fscanf (infile, "%*s%*s%*s%f", &data_val[ i ] ) ; Remember, to check for EOF, you could use the feof function. Instructor/Narrator: Here is example of the suppression operator which you may use in your program. Notice 3 strings of data are suppressed and the last floating-point value is kept and stored in an array data_val which is indexed by i. The value of i will have to be incremented for each data point stored. Also this array must be able to hold all data points when it is declared. Winter Quarter
Assignment E13 Find the largest value in the array and the smallest value in the array. Display the results on the screen, and also write the results to the output file, E13res.dat. The results to be displayed and printed are: The total number of data points in the file The maximum voltage and time at which it occurred The minimum voltage and time at which it occurred The elapsed time between the maximum and minimum values Instructor: Point out that the data itself doesn’t need to be displayed. Only these results need to be displayed. One method to acquire these results: -The total number of data points will be i, incremented after each data point is put into the array (from the previous slide). -The maximum voltage is stored when currentval>lastmax where currentval is the current value tested and lastmax is the previous maximum. Also store the index i whenever a new maximum is found (lastmaxi=currenti). Converting the index to time is discussed in the last result. -Obtain the minimum in the same manner as the maximum, but update the minimum when a new “smaller” number is found compared to the previous smallest number. Also store the index of this smallest number again. -By looking at the first couple data points, one can see the increment in time is .005 using the example data. To find the elapsed time between the minimum and maximum values one can simply determine the difference in their index (i values), then take the absolute value to be sure the value is positive, then multiply by the time increment. timediff=abs(lastmaxi-lastmini)*.005; Narrator: Once all the data has been input into the array, you are now required to find the largest and smallest value in the array. Display only the following results on the screen and in the input file, but not EVERY data point in the array: The total number of data points in the file, the maximum voltage and time it occurred, minimum voltage and when it occurred. For these times you only need to be concerned with the time relative to when the data acquisition began and not the time of day or date. You must finally calculate the time difference between this maximum and minimum value. Winter Quarter