Chapter 7 Arrays ================ The concept of an array is common to most programming languages. In an array, multiple values of the same data type can be stored with one variable name. The use of arrays allows for the development of smaller and more clearly readable programs. 7.1 Arrays ~~~~~~~~~~ Arrays are classified as aggregate data types. Unlike atomic data types which cannot be subdivied, aggregate data types are composed of one or more elements of an atomic or another aggregate data type. Simply put aggregate data types can be subdivided or broken down. To access individual elements of an array a subscript must be used. The subscript identifies the sub-element or component of the array that is to be accessed. Subscripts must start at **0** and proceed in increments of 1. Arrays can be of any data type supported by **C**, including constructed data types and arrays can be of any storage class except 'register'. 7.1.1 Declaring and Initialization ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Arrays can be declared as local variables or global variables. Initialization of an array when it is declared is allowed for both locally and globally declared arrays. Initializing an array when it is declared does not require the presence of a dimension on the array Fig. 7-1 ```````` :: char name[] = "John Smith"; int age[] = {21,32,43,22,25}; main() { . . . } OR main() { int age[] = {21,32,43,22,25}; . . . } If an array is declared to hold ten elements, the subscripts start at **0** and the highest subscript allowed for that array is **9**. There is no bounds checking on arrays. It is the programmers responsibility to keep the subscript within the bounds of the array. On the above array **age**, which has five elements if a statement such as :: age[6] = 10; is made in the program, no error or warning will be generated by the compiler. The array **age** should only support subscripts ranging from **0** to **5**. A subscript of **6** is not within the bounds of the array, but **C/C++** will allow the statement. In most cases the program will terminate with a runtime error because of the above statement. 7.1.2 Multi-Dimensional Arrays ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Two and three dimensional arrays are allowed but actually there is no limit on the number of dimensions for an array. :: int aver[10][20]; float sales[50][10][2]; Values in a multi-dimensional array are stored in row-major order, meaning that if array is declared as :: int data[3][4]; the values are stored in memory in this order :: [0][0] [0][1] [0][2] [0][3] [1][0] [1][1] [1][2] [1][3] ... and so forth. Therefore, when initializing a multi-dimensional array at declaration time, the row designator does not have to be stated, but the column length must be stated, such as: :: float monthlySalesByProduct[][4] = { {100.50, 234.32, 987.98} , {324.56, 123.43, 876.83} , {765.23, 8743.28, 27388.87} ... }; Notice the use of nested **{...}** pairs to set off the column values for a row. This is required in order to delineate the boundaries of the rows. The two-dimensional array above can also be called a square array. A square array can be used to store arrays of strings. Listing 7-1 ``````````` :: #include int main() { char students[10][25]; // holds names of students int idx; for( idx = 0; idx < 10; ++idx ) { cout << "Enter a students first name: "; cin >> students[idx]; } cout << "\nThe students in the class are:" << endl; for( idx = 0; idx < 10; ++idx ) cout << students[idx] << endl; return 0; } Notice the used of **students[i]** in the above example. The name of each row in the multi-dimensional array is the name of an array, which means that it represents the beginning address of where data is stored. You can use the name of a row the same as you would the name of an array. Also, notice that no matter how short the name of the student is, the same number of characters is allocated for each row, therefore leading to excessing use of memory space. 7.2 Arrays as Arguments ~~~~~~~~~~~~~~~~~~~~~~~ Arrays are automatically passed by reference to a function. The name of an array is the beginning address of where the array data is stored in memory; also known as a 'pointer constant'. Listing 7-2 ``````````` :: #include int main() { int list[10] ,max = 10 ,indx = 0 ,largest ,num_ele ; int find_max( int [], int ); while( indx < max) { cout << "\nEnter a number: "; cin >> list[indx]; ++indx; } largest = find_max( list, max ); cout << "\nLargest number is " << largest << endl; return 0; } int find_max( int list2[], int size ) { int idx ,highest ; for(highest = list2[0],idx = 1; idx < size; ++idx) if(highest < list2[idx]) highest = list2[idx]; return(highest); } In the **find_max()** function, **list2[]** is an array of undetermined length. On the call to **find_max()** the argument **list** is stated without subscripts, this passes the address of **list**, because **list** is an array. The name of an array is a pointer constant that represents the beginning address of the array. Therefore, when **list** is stated in the call to **find_max()**, the address that **list** represents is actually passed to the function. The receiving argument **list2[]** only receives the address of where the array begins in memory. With the array notation used for the receiving argument, **list2[]** can be treated as if it is an alias that allows for the direct manipulation of the data stored in **list**. Multi Dimensional arrays as arguments follow the same syntax except that on the receiving side the last dimension must be stated. This is so the compiler knows where one row of data ends and another row begins. Listing 7-3 ``````````` :: #include #define ROWS 10 #define COLUMNS 2 int main() { float agents[ROWS][COLUMNS]; int indx = 0; int maxex( float [][COLUMNS], int ); cout << "\nEnter 3-digit agent" << " number then travel " << "expenses(001 1642.50)"; do { cout << "\nAgents # and expenses:"; cin >> agents[indx][0] >> agents[indx][1]); }while( agents[indx++][0] != 0 ); indx--; indx = maxex(agents,indx); cout << "\nAgent with highest expenses: " << agents[indx][0]; cout << " Amount: %.2f" << agents[indx][1]; return 0; } int maxex(float list[][COLUMNS],int size) { int idx ,maxidx ; float max ; max = list[0][1]; maxidx = 0; for(idx = 1; idx < list[idx][1]) { max = list[idx][1]; maxidx = idx; } return(maxidx); } 7.3 Strings ``````````` An array of type char ending with a NULL ('\000') character is called a string in **C**. Fig 7-3 ``````` :: char name[] = "John Smith"; main() { . . . } the above string is stored in memory as: Fig 7-4 ``````` :: memory value address at address 2000 J 2001 o 2002 h 2003 n 2004 2005 S 2006 m 2007 i 2008 t 2009 h 200A \0 An array that will be used as a string must be declared with one additional byte more than is needed for data. This allots space for the NULL byte which is the signal indicating the end of the string. Listing 7-4 ``````````` :: // // looks at string in memory // #include #include #include int main() { char name[81]; int indx; cout << "\nEnter your name: "; gets(name); // used because of whitespace input cout << "\nThe string " << name << " is stored at address " << hex << unsigned(name); for(indx=0; indx < strlen(name); ++indx) { cout << "\nADDR: " << hex << unsigned(name); cout << " CHAR: " << name[indx]; cout << " DEC: " << dec << int(name[indx]); } return 0; } 7.3.1 String Functions< ~~~~~~~~~~~~~~~~~~~~~~~ Strings must be manipulated with standard functions supplied with the compiler function libraries. :: **char *strcat(char *str1, char *str2);** Appends str2 to str1, terminates the resulting string with a NULL character and returns the address of the concatenated string, str1. :: **char *strchr(char *str, char c);** Returns the address of the first occurrence of **c** in **str**; the function returns NULL if the character is not found. :: **int strcmp(char *str1, char *str2);** Compares **str1** and **str2** lexicographically and returns a value indicating their relationship. :: Value Meaning < 0 str1 less than str2 0 str1 equal to str2 > 0 str1 greater than str2 **int strcmpi(char *str1, char *str2);** Case-insensitive version of strcmp. Strings are compared without regard to case. :: **char *strcpy(char *str1, char *str2);** Copies **str2**, including the null terminating character to location specified by **str1** and returns **str1**. :: **int strcspn(char *str1, char *str2);** Returns the index of the first character in **str1** that belongs to the set of characters specified by **str2**. :: **char *strdup(char *str);** Allocates storage space for a copy of **str** and returns the address to that storage space containing the copied string. Returns NULL if storage could not be found. :: **int strlen(char *str);** Returns the length in bytes of **str** not including the termininating null character. :: **char *strlwr(char *str);** Converts any uppercase letters in the given null terminated string to lowercase. :: **char *strncat(char *str1, char *str2, int n);** Appends at most the first **n** characters of **str2** to **str1**, terminates the resulting string with a null character and returns the address of the concatenated **str1**. :: **int strncmp(char *str1, char *str2, int n);** Compares at most the first **n** characters of **str1** and **str2** lexicographically and returns a value indicating the relationship between the substrings. :: Value Meaning < 0 substring1 < substring2 0 substring1 = substring2 > 0 substring1 > substring2 **char *strncpy(char *str1, char * str2, int n);** Copies exactly **n** characters of **str2** to **str1** and returns **str1**. :: **char *strnset(char *str, char c, int n);** Sets at most the first **n** characters of **str** to the character **c** and returns the address of the altered **str**. :: **char *strtok( char *str, char *delims );** Searches one string for tokens, which are separated by delimiters defined in a second string. Listing 7-5 ``````````` :: #include #include int main() { char input[16] = "abc,d"; char *p; /* * strtok places a NULL terminator * in front of the token, if found */ p = strtok( input, "," ); if( p ) printf( "%s\n", p); /* * a second call to strtok using a * NULL as the first parameter returns * a pointer to the character following * the token */ p = strtok( NULL, "," ); if( p ) printf("%s\n", p ); return 0; }